﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область СлужебныйПрограммныйИнтерфейс

////////////////////////////////////////////////////////////////////////////////
// Основные процедуры и функции.

// Добавляет пользователя в группу доступа, соответствующую поставляемому профилю.
// Группа доступа определяется по идентификатору ссылки поставляемого профиля.
// Если группа доступа не будет найдена, она будет создана.
//
// Параметры:
//  Пользователь        - СправочникСсылка.Пользователи
//                      - СправочникСсылка.ВнешниеПользователи
//                      - СправочникСсылка.ГруппыПользователей
//                      - СправочникСсылка.ГруппыВнешнихПользователей - участник, которого нужно включить в группу доступа.
// 
//  ПоставляемыйПрофиль - Строка - строка идентификатора поставляемого профиля.
//                      - СправочникСсылка.ПрофилиГруппДоступа - ссылка на профиль, который
//                        создан по описанию в модуле УправлениеДоступомПереопределяемый
//                        в процедуре ПриЗаполненииПоставляемыхПрофилейГруппДоступа.
//                        Профили с непустым списком видов доступа не поддерживаются.
//                        Профиль групп доступа Администратор не поддерживается.
// 
Процедура ВключитьПользователяВГруппуДоступа(Пользователь, ПоставляемыйПрофиль) Экспорт
	
	ОбработатьСвязьПользователяСГруппойДоступа(Пользователь, ПоставляемыйПрофиль, Истина);
	
КонецПроцедуры

// Обновляет состав пользователей указанных групп исполнителей.
// 
// Требуется вызывать при изменении состава пользователей у групп исполнителей,
// например, у групп исполнителей задач.
//
// В качестве значений параметра передается группы исполнителей, состав которых изменился.
//
// Параметры:
//  ГруппыИсполнителей - СправочникСсылка.ГруппыИсполнителейЗадач - одна группа,
//                     - Массив из СправочникСсылка.ГруппыИсполнителейЗадач - несколько групп,
//                     - Неопределено - без отбора.
//
Процедура ОбновитьПользователейГруппИсполнителей(ГруппыИсполнителей = Неопределено) Экспорт
	
	Если ТипЗнч(ГруппыИсполнителей) = Тип("Массив") И ГруппыИсполнителей.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Параметры = Новый Структура;
	Параметры.Вставить("ГруппыИсполнителей", ГруппыИсполнителей);
	
	РегистрыСведений.ГруппыЗначенийДоступа.ОбновитьГруппировкиПользователей(Параметры);
	
КонецПроцедуры

// Проверяет существование вида доступа с указанным именем.
// Применяется для автоматизации условного встраивания подсистем.
// 
// Параметры:
//   ИмяВидаДоступа - Строка - имя вида доступа.
// 
// Возвращаемое значение:
//  Структура:
//
Функция ВидДоступаСуществует(ИмяВидаДоступа) Экспорт
	
	Возврат СвойстваВидаДоступа(ИмяВидаДоступа) <> Неопределено;
	
КонецФункции

// Возвращает вид интерфейса пользователя для настройки доступа.
//
// Возвращаемое значение:
//  Булево
//
Функция УпрощенныйИнтерфейсНастройкиПравДоступа() Экспорт
	
	УпрощенныйИнтерфейс = Ложь;
	УправлениеДоступомПереопределяемый.ПриОпределенииИнтерфейсаНастройкиДоступа(УпрощенныйИнтерфейс);
	
	Возврат УпрощенныйИнтерфейс = Истина;
	
КонецФункции

// Возвращает массив разрешенных значений указанных типов в рамках всех групп доступа.
// Используется в процедуре НастроитьОтборыДинамическогоСписка для ускорения открытия динамических списков.
// 
// Параметры:
//  Таблица      - Строка - полное имя объекта метаданных, например, "Документ.РасходнаяНакладная".
//  ТипЗначений  - Тип    - тип значений доступа, разрешенные значения которых нужно вернуть.
//               - Массив - массив указанных выше типов.
//
//  Значения     - Неопределено - не учитывать.
//               - Массив - массив значений типов, указанных в параметре ТипЗначений.
//
//  Пользователь - Неопределено - вернуть разрешенные значения для авторизованного пользователя.
//               - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи - вернуть
//                   разрешенные значения для указанного пользователя.
//
//  ВернутьВсе   - Булево - если установить Истина, тогда будут возвращены все значение даже тогда,
//                   когда их более 100.
//
// Возвращаемое значение:
//  Неопределено - либо все значения разрешены для типов, указанных в параметре ТипЗначений,
//                 либо (когда ВернутьВсе = Ложь) количество разрешенных значений превышает 100.
//  Массив       - ссылки разрешенных значений указанных типов.
//
Функция РазрешенныеЗначенияДляДинамическогоСписка(Таблица, ТипЗначений, Значения = Неопределено, Пользователь = Неопределено, ВернутьВсе = Ложь) Экспорт
	
	Если ТипЗнч(ТипЗначений) <> Тип("Массив") Тогда
		ТипыЗначений = Новый Массив;
		ТипыЗначений.Добавить(ТипЗначений);
		
	ИначеЕсли ТипЗначений.Количество() = 0 Тогда
		Возврат Неопределено;
	Иначе
		ТипыЗначений = ТипЗначений;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	ТекстЗапросаЗначенийБезГрупп =
	"ВЫБРАТЬ ПЕРВЫЕ 101
	|	ЗначенияБезГрупп.Ссылка КАК Ссылка
	|ИЗ
	|	(ВЫБРАТЬ
	|		&ЗначениеПустойСсылки КАК Ссылка
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		ЗначенияБезПустойСсылки.Ссылка
	|	ИЗ
	|		&ТаблицаЗначенийДоступа КАК ЗначенияБезПустойСсылки) КАК ЗначенияБезГрупп
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				ГруппыДоступаПользователя КАК ГруппыДоступа
	|			ГДЕ
	|				ВЫБОР
	|					КОГДА ИСТИНА В
	|							(ВЫБРАТЬ ПЕРВЫЕ 1
	|								ИСТИНА
	|							ИЗ
	|								РегистрСведений.ЗначенияГруппДоступа КАК Значения
	|							ГДЕ
	|								Значения.ГруппаДоступа = ГруппыДоступа.Ссылка
	|								И Значения.ЗначениеДоступа = ЗначенияБезГрупп.Ссылка)
	|						ТОГДА ИСТИНА
	|					ИНАЧЕ ЛОЖЬ
	|				КОНЕЦ = ВЫБОР
	|					КОГДА ИСТИНА В
	|							(ВЫБРАТЬ ПЕРВЫЕ 1
	|								ИСТИНА
	|							ИЗ
	|								РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|							ГДЕ
	|								ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступа.Ссылка
	|								И ТИПЗНАЧЕНИЯ(ЗначенияПоУмолчанию.ТипЗначенийДоступа) = ТИПЗНАЧЕНИЯ(ЗначенияБезГрупп.Ссылка)
	|								И ЗначенияПоУмолчанию.ВсеРазрешены = ЛОЖЬ)
	|						ТОГДА ИСТИНА
	|					ИНАЧЕ ЛОЖЬ
	|				КОНЕЦ)
	|	И ЗначенияБезГрупп.Ссылка В(&Значения)";
	
	Если ТипЗнч(Значения) = Тип("Массив") Или ВернутьВсе Тогда
		ТекстЗапросаЗначенийБезГрупп = СтрЗаменить(ТекстЗапросаЗначенийБезГрупп, "ПЕРВЫЕ 101", ""); // @query-part-1
	КонецЕсли;
	Если ТипЗнч(Значения) <> Тип("Массив") Тогда
		ТекстЗапросаЗначенийБезГрупп = СтрЗаменить(ТекстЗапросаЗначенийБезГрупп, "ЗначенияБезГрупп.Ссылка В(&Значения)", "ИСТИНА"); // @query-part-1
	КонецЕсли;
	
	ТекстЗапросаЗначенийСГруппами =
	"ВЫБРАТЬ ПЕРВЫЕ 101
	|	ЗначенияСГруппами.Ссылка КАК Ссылка
	|ИЗ
	|	(ВЫБРАТЬ
	|		&ЗначениеПустойСсылки КАК Ссылка
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		ЗначенияБезПустойСсылки.Ссылка
	|	ИЗ
	|		&ТаблицаЗначенийДоступа КАК ЗначенияБезПустойСсылки) КАК ЗначенияСГруппами
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				ГруппыДоступаПользователя КАК ГруппыДоступа
	|			ГДЕ
	|				ВЫБОР
	|					КОГДА ИСТИНА В
	|							(ВЫБРАТЬ ПЕРВЫЕ 1
	|								ИСТИНА
	|							ИЗ
	|								РегистрСведений.ЗначенияГруппДоступа КАК Значения
	|									ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ГруппыЗначенийДоступа КАК ГруппыЗначений
	|									ПО
	|										Значения.ГруппаДоступа = ГруппыДоступа.Ссылка
	|											И Значения.ЗначениеДоступа = ГруппыЗначений.ГруппаЗначенийДоступа
	|											И ГруппыЗначений.ЗначениеДоступа = ЗначенияСГруппами.Ссылка)
	|						ТОГДА ИСТИНА
	|					ИНАЧЕ ЛОЖЬ
	|				КОНЕЦ = ВЫБОР
	|					КОГДА ИСТИНА В
	|							(ВЫБРАТЬ ПЕРВЫЕ 1
	|								ИСТИНА
	|							ИЗ
	|								РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|							ГДЕ
	|								ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступа.Ссылка
	|								И ТИПЗНАЧЕНИЯ(ЗначенияПоУмолчанию.ТипЗначенийДоступа) = ТИПЗНАЧЕНИЯ(ЗначенияСГруппами.Ссылка)
	|								И ЗначенияПоУмолчанию.ВсеРазрешены = ЛОЖЬ)
	|						ТОГДА ИСТИНА
	|					ИНАЧЕ ЛОЖЬ
	|				КОНЕЦ)
	|	И ЗначенияСГруппами.Ссылка В(&Значения)";
	
	Если ТипЗнч(Значения) = Тип("Массив") Или ВернутьВсе Тогда
		ТекстЗапросаЗначенийСГруппами = СтрЗаменить(ТекстЗапросаЗначенийСГруппами, "ПЕРВЫЕ 101", ""); // @query-part-1
	КонецЕсли;
	Если ТипЗнч(Значения) <> Тип("Массив") Тогда
		ТекстЗапросаЗначенийСГруппами = СтрЗаменить(ТекстЗапросаЗначенийСГруппами, "ЗначенияСГруппами.Ссылка В(&Значения)", "ИСТИНА"); // @query-part-1
	КонецЕсли;
	
	ТекстЗапросаВсеЗначения =
	"ВЫБРАТЬ ПЕРВЫЕ 101
	|	ВсеЗначения.Ссылка КАК Ссылка
	|ИЗ
	|	(ВЫБРАТЬ
	|		&ЗначениеПустойСсылки КАК Ссылка
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		ЗначенияБезПустойСсылки.Ссылка
	|	ИЗ
	|		&ТаблицаЗначенийДоступа КАК ЗначенияБезПустойСсылки) КАК ВсеЗначения
	|ГДЕ
	|	ВсеЗначения.Ссылка В(&Значения)";
	
	Если ТипЗнч(Значения) = Тип("Массив") Или ВернутьВсе Тогда
		ТекстЗапросаВсеЗначения = СтрЗаменить(ТекстЗапросаВсеЗначения, "ПЕРВЫЕ 101", ""); // @query-part-1
	КонецЕсли;
	Если ТипЗнч(Значения) <> Тип("Массив") Тогда
		ТекстЗапросаВсеЗначения = СтрЗаменить(ТекстЗапросаВсеЗначения, "ВсеЗначения.Ссылка В(&Значения)", "ИСТИНА"); // @query-part-1
	КонецЕсли;
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	ТекстЗапроса = "";
	Если Пользователь <> Неопределено Тогда
		АвторизованныйПользователь = Пользователь;
	Иначе
		АвторизованныйПользователь = Пользователи.АвторизованныйПользователь();
	КонецЕсли;
	ДобавитьТекущегоПользователя = Ложь;
	ЕстьИспользуемыеВидыДоступа = Ложь;
	ИспользуемыеВидыДоступа = ИспользуемыеВидыДоступа();
	
	Для Каждого ТекущийТип Из ТипыЗначений Цикл
		Свойства = СвойстваВидовДоступа.ПоТипамЗначений.Получить(ТекущийТип); // См. СвойстваВидаДоступа
		Если Свойства = Неопределено Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Тип ""%1"" не является типом значений доступа'"), Строка(ТекущийТип));
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		Если ИспользуемыеВидыДоступа.Получить(Свойства.Ссылка) = Неопределено Тогда
			ТекущийТекстЗапроса = ТекстЗапросаВсеЗначения;
		Иначе
			Если СвойстваВидовДоступа.ТипыЗначенийДоступаСГруппами.Получить(ТекущийТип) = Неопределено Тогда
				ТекущийТекстЗапроса = ТекстЗапросаЗначенийБезГрупп;
			Иначе
				ТекущийТекстЗапроса = ТекстЗапросаЗначенийСГруппами;
			КонецЕсли;
			ЕстьИспользуемыеВидыДоступа = Истина;
		КонецЕсли;
		ТаблицаЗначенийДоступа = Метаданные.НайтиПоТипу(ТекущийТип).ПолноеИмя();
		ТекущийТекстЗапроса = СтрЗаменить(ТекущийТекстЗапроса, "&ТаблицаЗначенийДоступа", ТаблицаЗначенийДоступа);
		ТекущийТекстЗапроса = СтрЗаменить(ТекущийТекстЗапроса, "&ЗначениеПустойСсылки",
			"ЗНАЧЕНИЕ(" + ТаблицаЗначенийДоступа + ".ПустаяСсылка)"); // @query-part-1
		Если ЗначениеЗаполнено(ТекстЗапроса) Тогда
			ОбъединитьЗапросСЗапросом(ТекстЗапроса, ТекущийТекстЗапроса);
		Иначе
			ТекстЗапроса = ТекущийТекстЗапроса;
		КонецЕсли;
		Если ТекущийТип = ТипЗнч(АвторизованныйПользователь) Тогда
			ДобавитьТекущегоПользователя = Истина;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ЕстьИспользуемыеВидыДоступа Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИмяОсновнойТаблицыСписка", Таблица);
	Запрос.УстановитьПараметр("АвторизованныйПользователь", АвторизованныйПользователь);
	Запрос.Текст = ТекстЗапросаГруппДоступа();
	Если ТипЗнч(Значения) = Тип("Массив") Тогда
		Запрос.УстановитьПараметр("Значения", Значения);
	КонецЕсли;
	
	ДобавитьЗапросВПакет(Запрос.Текст, ТекстЗапроса);
	
	Выгрузка = Запрос.Выполнить().Выгрузить();
	
	Если ТипЗнч(Значения) <> Тип("Массив")
	   И Не ВернутьВсе
	   И Выгрузка.Количество() > 100 Тогда
		
		Возврат Неопределено;
	КонецЕсли;
	
	РазрешенныеЗначения = Выгрузка.ВыгрузитьКолонку("Ссылка");
	
	Если ДобавитьТекущегоПользователя Тогда
		РазрешенныеЗначения.Добавить(АвторизованныйПользователь);
	КонецЕсли;
	
	Возврат РазрешенныеЗначения;
	
КонецФункции

// Добавляет администраторов системы в группу доступа,
// связанную с предопределенным профилем ОткрытиеВнешнихОтчетовИОбработок.
//
Процедура УстановитьПравоОткрытияВнешнихОтчетовИОбработок(ОткрытиеРазрешено) Экспорт
	
	СвойстваПрофиля = ОписаниеПрофиляОткрытиеВнешнихОтчетовИОбработок();
	СвойстваПрофиля.Вставить("Ссылка", Справочники.ПрофилиГруппДоступа.ПоставляемыйПрофильПоИдентификатору(
		СвойстваПрофиля.Имя, Истина));
	
	ИдентификаторРоли = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		Метаданные.Роли.ИнтерактивноеОткрытиеВнешнихОтчетовИОбработок);
	
	// В упрощенном интерфейсе группу доступа создавать не требуется (только профиль).
	УпрощенныйИнтерфейс = УпрощенныйИнтерфейсНастройкиПравДоступа();
	Если Не УпрощенныйИнтерфейс Тогда
		ГруппаПрофиля = ГруппаДоступаОткрытиеВнешнихОтчетовИОбработок(СвойстваПрофиля);
	КонецЕсли;
	
	Если ОткрытиеРазрешено Тогда
		// Включение администраторов в группы доступа с этим профилем.
		РольАдминистратора = Метаданные.Роли.АдминистраторСистемы;
		ПользователиИБ = ПользователиИнформационнойБазы.ПолучитьПользователей();
		СоставПользователей = Новый Массив;
		Для Каждого ПользовательИБ Из ПользователиИБ Цикл
			Если ПользовательИБ.Роли.Содержит(РольАдминистратора) Тогда
				Пользователь = Справочники.Пользователи.НайтиПоРеквизиту(
					"ИдентификаторПользователяИБ",
					ПользовательИБ.УникальныйИдентификатор);
				Если Пользователь = Неопределено Тогда
					Продолжить;
				КонецЕсли;
				Если УпрощенныйИнтерфейс Тогда
					// В упрощенном интерфейсе каждый администратор включаются в отдельную группу.
					УправлениеДоступом.ВключитьПрофильПользователю(Пользователь, СвойстваПрофиля.Ссылка);
				Иначе
					// В расширенном интерфейсе администраторы включаются в одну группу, связанную с предопределенным профилем.
					СоставПользователей.Добавить(Пользователь);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Если Не УпрощенныйИнтерфейс Тогда
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
			ЭлементБлокировки.УстановитьЗначение("Ссылка", ГруппаПрофиля);
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				ГруппаДоступаОбъект = ГруппаПрофиля.ПолучитьОбъект();
				Для Каждого Пользователь Из СоставПользователей Цикл
					Если ГруппаДоступаОбъект.Пользователи.Найти(Пользователь, "Пользователь") = Неопределено Тогда
						ГруппаДоступаОбъект.Пользователи.Добавить().Пользователь = Пользователь;
					КонецЕсли;
				КонецЦикла;
				Если ГруппаДоступаОбъект.Модифицированность() Тогда
					ГруппаДоступаОбъект.Записать();
				КонецЕсли;
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЕсли;
	Иначе
		// Удаление роли из всех профилей, за исключением предопределенного.
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ПрофилиГруппДоступаРоли.Ссылка
		|ИЗ
		|	Справочник.ПрофилиГруппДоступа.Роли КАК ПрофилиГруппДоступаРоли
		|ГДЕ
		|	ПрофилиГруппДоступаРоли.Роль = &Роль
		|	И ПрофилиГруппДоступаРоли.Ссылка <> &ИсключаемыйПрофиль";
		Запрос.УстановитьПараметр("Роль", ИдентификаторРоли);
		Запрос.УстановитьПараметр("ИсключаемыйПрофиль", СвойстваПрофиля.Ссылка);
		МассивПрофилей = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
		Для Каждого Профиль Из МассивПрофилей Цикл
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
			ЭлементБлокировки.УстановитьЗначение("Ссылка", Профиль);
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				ПрофильОбъект = Профиль.ПолучитьОбъект();
				Найденные = ПрофильОбъект.Роли.НайтиСтроки(Новый Структура("Роль", ИдентификаторРоли));
				Для Каждого СтрокаТаблицы Из Найденные Цикл
					ПрофильОбъект.Роли.Удалить(СтрокаТаблицы);
				КонецЦикла;
				ПрофильОбъект.Записать();
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЦикла;
		
		// Очистка состава групп доступа, связанных с предопределенным профилем.
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ГруппыДоступа.Ссылка
		|ИЗ
		|	Справочник.ГруппыДоступа КАК ГруппыДоступа
		|ГДЕ
		|	ГруппыДоступа.Профиль = &Профиль";
		Запрос.УстановитьПараметр("Профиль", СвойстваПрофиля.Ссылка);
		МассивГрупп = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
		Для Каждого ГруппаДоступа Из МассивГрупп Цикл
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
			ЭлементБлокировки.УстановитьЗначение("Ссылка", ГруппаДоступа);
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				ГруппаДоступаОбъект = ГруппаДоступа.ПолучитьОбъект();
				ГруппаДоступаОбъект.Пользователи.Очистить();
				ГруппаДоступаОбъект.Записать();
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура ПриРегистрацииИспользованияВерсииРасширенийВНеразделенномСеансе() Экспорт
	
	Если ТекущийРежимЗапуска() = Неопределено
	 Или ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	ОтключитьУВсехРасширенийФлажокИспользоватьОсновныеРолиДляВсехПользователей();
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Возвращаемое значение:
//  Булево
//
Функция ВариантВстроенногоЯзыкаРусский() Экспорт
	
	Возврат Метаданные.ВариантВстроенногоЯзыка = Метаданные.СвойстваОбъектов.ВариантВстроенногоЯзыка.Русский;
	
КонецФункции

// Обновляет список ролей пользователей информационной базы
// по их текущим принадлежностям к группам доступа.
//  Пользователи с ролью "ПолныеПрава" игнорируется.
//
// Параметры:
//  ОписаниеПользователей - СправочникСсылка.Пользователи
//                        - СправочникСсылка.ВнешниеПользователи
//                        - Массив - значений указанных выше типов.
//                        - Неопределено - обновить роли всех пользователей.
//                        - Тип - по которому будет найден объект метаданных:
//                          если будет найден Справочник.ВнешниеПользователи,
//                          то будут обновлены роли всех внешних пользователей,
//                          иначе будут обновлены роли всех пользователей.
//
//  ПарольПользователяСервиса - Строка - пароль для авторизации в менеджере сервиса.
//
//  ЕстьИзменения - Булево - возвращаемое значение. В этот параметр возвращается
//                  значение Истина, если производилась запись, иначе не изменяется.
//
Процедура ОбновитьРолиПользователей(Знач ОписаниеПользователей = Неопределено,
                                    Знач ПарольПользователяСервиса = Неопределено,
                                    ЕстьИзменения = Ложь) Экспорт
	
	Если НЕ ПользователиСлужебный.ЗапретРедактированияРолей() Тогда
		// Роли устанавливаются механизмами подсистем Пользователи и ВнешниеПользователи.
		Возврат;
	КонецЕсли;
	
	Если ОписаниеПользователей = Неопределено Тогда
		МассивПользователей = Неопределено;
		Пользователи.НайтиНеоднозначныхПользователейИБ(Неопределено);
		
	ИначеЕсли ТипЗнч(ОписаниеПользователей) = Тип("Массив") Тогда
		МассивПользователей = ОписаниеПользователей;
		Если МассивПользователей.Количество() = 0 Тогда
			Возврат;
		ИначеЕсли МассивПользователей.Количество() = 1 Тогда
			Пользователи.НайтиНеоднозначныхПользователейИБ(МассивПользователей[0]);
		Иначе
			Пользователи.НайтиНеоднозначныхПользователейИБ(Неопределено);
		КонецЕсли;
		
	ИначеЕсли ТипЗнч(ОписаниеПользователей) = Тип("Тип") Тогда
		МассивПользователей = ОписаниеПользователей;
		Пользователи.НайтиНеоднозначныхПользователейИБ(Неопределено);
	Иначе
		МассивПользователей = Новый Массив;
		МассивПользователей.Добавить(ОписаниеПользователей);
		Пользователи.НайтиНеоднозначныхПользователейИБ(ОписаниеПользователей);
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	
	ТекущиеСвойстваПользователей = ТекущиеСвойстваПользователей(МассивПользователей);
	
	// Параметры проверки в цикле.
	ВсеРоли                             = ПользователиСлужебный.ВсеРоли().Соответствие;
	ИдентификаторыПользователейИБ       = ТекущиеСвойстваПользователей.ИдентификаторыПользователейИБ;
	НовыеРолиПользователей              = ТекущиеСвойстваПользователей.РолиПользователей;
	Администраторы                      = ТекущиеСвойстваПользователей.Администраторы;
	РазделениеВключено                  = ОбщегоНазначения.РазделениеВключено();
	НеобходимоОбновлениеИБ              = ОбновлениеИнформационнойБазы.НеобходимоОбновлениеИнформационнойБазы();
	ИдентификаторТекущегоПользователяИБ = ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор;
	
	ОбязательныеРолиАдминистратора = Новый Соответствие;
	ОбязательныеРолиАдминистратора.Вставить("ПолныеПрава", Истина);
	Если Не РазделениеВключено Тогда
		ОбязательныеРолиАдминистратора.Вставить("АдминистраторСистемы", Истина);
	КонецЕсли;
	СтандартныеРолиРасширений = УправлениеДоступомСлужебныйПовтИсп.ОписаниеСтандартныхРолейРасширенийСеанса().РолиСеанса;
	ДополнительныеРолиАдминистратора = Новый Соответствие(СтандартныеРолиРасширений.ДополнительныеРолиАдминистратора);
	ПриПодготовкеДополнительныхРолейАдминистратора(ДополнительныеРолиАдминистратора);
	ДополнительныеРолиАдминистратора.Вставить("ИнтерактивноеОткрытиеВнешнихОтчетовИОбработок", Истина);
	
	// Будущий итог после цикла.
	НовыеАдминистраторыИБ     = Новый Соответствие;
	ОбновляемыеПользователиИБ = Новый Соответствие;
	НекорректныеРоли          = НовыеНекорректныеРоли(НовыеРолиПользователей);
	
	Для Каждого ОписаниеПользователя Из ИдентификаторыПользователейИБ Цикл
		
		ТекущийПользователь         = ОписаниеПользователя.Пользователь;
		ИдентификаторПользователяИБ = ОписаниеПользователя.ИдентификаторПользователяИБ;
		НовыйАдминистраторИБ        = Ложь;
		
		// Поиск пользователя ИБ.
		Если ТипЗнч(ИдентификаторПользователяИБ) = Тип("УникальныйИдентификатор") Тогда
			ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
				ИдентификаторПользователяИБ);
		Иначе
			ПользовательИБ = Неопределено;
		КонецЕсли;
		
		Если ПользовательИБ = Неопределено
		 Или Не ЗначениеЗаполнено(ПользовательИБ.Имя) Тогда
			Продолжить;
		КонецЕсли;
		
		Если НеобходимоОбновлениеИБ
		   И ИдентификаторПользователяИБ = ИдентификаторТекущегоПользователяИБ Тогда
			Продолжить;
		КонецЕсли;
		
		Отказ = Ложь;
		ИнтеграцияПодсистемБСП.ПриОбновленииРолейПользователяИБ(ИдентификаторПользователяИБ, Отказ);
		Если Отказ Тогда
			Продолжить;
		КонецЕсли;
		
		Отбор = Новый Структура("Пользователь", ТекущийПользователь);
		НовыеРоли = НовыеРолиПользователей.Скопировать(
			НовыеРолиПользователей.НайтиСтроки(Отбор), "Роль, РольСсылка");
		
		НовыеРоли.Индексы.Добавить("Роль");
		
		Если Администраторы[ТекущийПользователь] <> Неопределено Тогда
			ТекущиеНовыеРоли = НовыеРоли;
			НовыеРоли = ТекущиеНовыеРоли.Скопировать(Новый Массив);
			Для Каждого КлючИЗначение Из ОбязательныеРолиАдминистратора Цикл
				НовыеРоли.Добавить().Роль = КлючИЗначение.Ключ;
			КонецЦикла;
			Для Каждого КлючИЗначение Из ДополнительныеРолиАдминистратора Цикл
				Если ТекущиеНовыеРоли.Найти(КлючИЗначение.Ключ, "Роль") = Неопределено Тогда
					Продолжить;
				КонецЕсли;
				НовыеРоли.Добавить().Роль = КлючИЗначение.Ключ;
			КонецЦикла;
		КонецЕсли;
		
		// Проверка старых ролей.
		СтарыеРоли        = Новый Соответствие;
		РолиДляДобавления = Новый Соответствие;
		РолиДляУдаления   = Новый Соответствие;
		
		Для Каждого Роль Из ПользовательИБ.Роли Цикл
			ИмяРоли = Роль.Имя;
			СтарыеРоли.Вставить(ИмяРоли, Истина);
			Если НовыеРоли.Найти(ИмяРоли, "Роль") = Неопределено Тогда
				РолиДляУдаления.Вставить(ИмяРоли, Роль);
			КонецЕсли;
		КонецЦикла;
		
		НедоступныеРоли = ПользователиСлужебный.НедоступныеРолиПоТипуПользователя(
			ТипЗнч(ТекущийПользователь) = Тип("СправочникСсылка.ВнешниеПользователи"));
		
		// Проверка новых ролей.
		Для Каждого Строка Из НовыеРоли Цикл
			
			Если СтарыеРоли[Строка.Роль] <> Неопределено Тогда
				Если РазделениеВключено
				   И НедоступныеРоли.Получить(Строка.Роль) <> Неопределено Тогда
					ДобавитьНекорректнуюРоль(НекорректныеРоли, Строка, ТекущийПользователь, Ложь);
					РолиДляУдаления.Вставить(Строка.Роль, Истина);
				КонецЕсли;
				Продолжить;
			КонецЕсли;
			
			Если ВсеРоли.Получить(Строка.Роль) = Неопределено Тогда
				ДобавитьНекорректнуюРоль(НекорректныеРоли, Строка, ТекущийПользователь, Истина);
				Продолжить;
			КонецЕсли;
			
			Если НедоступныеРоли.Получить(Строка.Роль) <> Неопределено Тогда
				ДобавитьНекорректнуюРоль(НекорректныеРоли, Строка, ТекущийПользователь, Ложь);
				Продолжить;
			КонецЕсли;
			
			РолиДляДобавления.Вставить(Строка.Роль, Истина);
			
			Если Строка.Роль = "АдминистраторСистемы" Тогда
				НовыйАдминистраторИБ = Истина;
			КонецЕсли;
		КонецЦикла;
		
		// Завершение обработки текущего пользователя.
		Если РолиДляДобавления.Количество() = 0
		   И РолиДляУдаления.Количество()   = 0 Тогда
			Продолжить;
		КонецЕсли;
		
		ИзмененияРолей = Новый Структура;
		ИзмененияРолей.Вставить("ПользовательСсылка", ТекущийПользователь);
		ИзмененияРолей.Вставить("ПользовательИБ",     ПользовательИБ);
		ИзмененияРолей.Вставить("РолиДляДобавления",  РолиДляДобавления);
		ИзмененияРолей.Вставить("РолиДляУдаления",    РолиДляУдаления);
		
		Если НовыйАдминистраторИБ Тогда
			НовыеАдминистраторыИБ.Вставить(ТекущийПользователь, ИзмененияРолей);
		Иначе
			ОбновляемыеПользователиИБ.Вставить(ТекущийПользователь, ИзмененияРолей);
		КонецЕсли;
		
		ЕстьИзменения = Истина;
	КонецЦикла;
	
	ЗарегистрироватьНекорректныеРоли(НекорректныеРоли);
	
	// Добавление новых администраторов.
	Если НовыеАдминистраторыИБ.Количество() > 0 Тогда
		ОбновитьРолиПользователейИБ(НовыеАдминистраторыИБ, ПарольПользователяСервиса);
	КонецЕсли;
	
	// Удаление старых администраторов и обновление остальных пользователей.
	Если ОбновляемыеПользователиИБ.Количество() > 0 Тогда
		ОбновитьРолиПользователейИБ(ОбновляемыеПользователиИБ, ПарольПользователяСервиса);
	КонецЕсли;
	
	ОтключитьУВсехРасширенийФлажокИспользоватьОсновныеРолиДляВсехПользователей();
	
КонецПроцедуры

#Область УниверсальноеОграничение

Функция ОграничиватьДоступНаУровнеЗаписейУниверсально(СУчетомКонстантыОграничениеДоступаНаУровнеЗаписей = Истина,
			КогдаПервоеОбновлениеДоступаЗавершилось = Ложь, ОбновлятьПараметрыСеансаКогдаТребуется = Истина) Экспорт
	
	Если СУчетомКонстантыОграничениеДоступаНаУровнеЗаписей Тогда
		Значение = КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально();
		
		Если ОбновлятьПараметрыСеансаКогдаТребуется
		   И (Значение <> УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально()
		      Или Значение
		        И Не ПараметрыСеанса.ОграничениеДоступаНаУровнеЗаписейУниверсально
		        И КонстантаПервоеОбновлениеДоступаЗавершилось()) Тогда
			
			ОбновитьПараметрыСеанса();
			Значение = УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально();
		КонецЕсли;
		
		Возврат Значение
		      И (Не КогдаПервоеОбновлениеДоступаЗавершилось
		         Или КонстантаПервоеОбновлениеДоступаЗавершилось());
	КонецЕсли;
	
	Если ОбновлятьПараметрыСеансаКогдаТребуется
	   И Не ПараметрыСеанса.ОтключениеОбновленияКлючейДоступа.Полное
	   И Не ПараметрыСеанса.ОграничениеДоступаНаУровнеЗаписейУниверсально Тогда
		
		ПоследняяПроверка = УправлениеДоступомСлужебныйПовтИсп.ПоследняяПроверкаВерсииРазрешенныхНаборов();
		Если ПоследняяПроверка.Дата + 3 <= ТекущаяДатаСеанса() Тогда
			
			Значение = КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально();
			Если Значение И КонстантаПервоеОбновлениеДоступаЗавершилось()
			 Или Значение <> УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
				
				ОбновитьПараметрыСеанса();
				Значение = УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально();
				ПоследняяПроверка = УправлениеДоступомСлужебныйПовтИсп.ПоследняяПроверкаВерсииРазрешенныхНаборов();
			КонецЕсли;
			ПоследняяПроверка.Дата = ТекущаяДатаСеанса();
		КонецЕсли;
	КонецЕсли;
	
	Если Не КогдаПервоеОбновлениеДоступаЗавершилось Тогда
		Возврат УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально();
	КонецЕсли;
	
	Возврат ПараметрыСеанса.ОграничениеДоступаНаУровнеЗаписейУниверсально;
	
КонецФункции

Функция КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально() Экспорт
	
	Значение = Константы.ОграничиватьДоступНаУровнеЗаписейУниверсально.Получить();
	
	Если Не Значение И Не ВариантВстроенногоЯзыкаРусский() Тогда
		Значение = Истина;
		УстановитьОтключениеБезопасногоРежима(Истина);
		УстановитьПривилегированныйРежим(Истина);
		МенеджерЗначения = СлужебныйМенеджерЗначения(
			Константы.ОграничиватьДоступНаУровнеЗаписейУниверсально);
		МенеджерЗначения.Значение = Значение;
		МенеджерЗначения.Записать();
		УстановитьПривилегированныйРежим(Ложь);
		УстановитьОтключениеБезопасногоРежима(Ложь);
	КонецЕсли;
	
	Возврат Значение;
	
КонецФункции

// Устанавливает использование регламентного задания ОбновлениеДоступа.
//
// Параметры:
//   Использование - Булево - Истина, если задание нужно включить, иначе Ложь.
//   БезПроверкиВыполненияОбновленияИБ - Булево
//
Процедура УстановитьОбновлениеДоступа(Использование, БезПроверкиВыполненияОбновленияИБ = Ложь) Экспорт
	
	Если Использование
	   И Не БезПроверкиВыполненияОбновленияИБ
	   И ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы() Тогда
		
		Возврат; // После обновления ИБ задание включается безусловно.
	КонецЕсли;
	
	Если Использование Тогда
		ПоследнееОбновлениеДоступа = ПоследнееОбновлениеДоступа();
		Если ПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	ВключитьЗадание = ?(ОграничиватьДоступНаУровнеЗаписейУниверсально(), Использование, Ложь);
	
	Отбор = Новый Структура("Метаданные", Метаданные.РегламентныеЗадания.ОбновлениеДоступаНаУровнеЗаписей);
	Задания = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
	
	ТребуетсяИзменение = Ложь;
	Для Каждого Задание Из Задания Цикл
		Если ВключитьЗадание <> Задание.Использование Тогда
			ТребуетсяИзменение = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ТребуетсяИзменение Тогда
		Возврат;
	КонецЕсли;
	
	Если ВключитьЗадание И ЭтоСеансФоновогоОбновленияДоступа() Тогда
		Возврат;
	КонецЕсли;
	
	Если МонопольныйРежим()
	 Или Не ТранзакцияАктивна()
	 Или ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		
		ИзменитьРегламентноеЗаданиеОбновленияДоступа(ВключитьЗадание);
		
	ИначеЕсли ВключитьЗадание Тогда
		ФоновыеЗадания.Выполнить("УправлениеДоступомСлужебный.ВключитьРегламентноеЗаданиеОбновленияДоступа",,,
			НСтр("ru = 'Управление доступом: Включение регламентного задания обновления доступа'",
				ОбщегоНазначения.КодОсновногоЯзыка()));
	Иначе
		ФоновыеЗадания.Выполнить("УправлениеДоступомСлужебный.ОтключитьРегламентноеЗаданиеОбновленияДоступа",,,
			НСтр("ru = 'Управление доступом: Отключение регламентного задания обновления доступа'",
				ОбщегоНазначения.КодОсновногоЯзыка()));
	КонецЕсли;
	
КонецПроцедуры

// Возвращает ошибки текстов ограничений доступа объектов без учета зависимостей между объектами.
// Тексты проверяются в режиме максимальных ограничений (как будто включены все виды ограничений).
// Функция должна вызываться перед функцией НастройкиВнедрения, чтобы собрать весь пакет ошибок.
//
// Возвращаемое значение:
//  Массив из Структура:
//    * ПолноеИмя   - Строка - полное имя объекта метаданных.
//    * ТекстОшибки - Строка - текст ошибки в ограничении доступа.
//
Функция ОшибкиОграниченийДоступа() Экспорт
	
	Ошибки = Новый Массив;
	ОбщийКонтекст = ОбщийКонтекстРасчетаПараметровОграничения(, Истина, Ложь);
	
	Попытка
		СпискиСОграничением = УправлениеДоступомСлужебныйПовтИсп.СпискиСОграничением();
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Списки с ограничением доступа некорректно указаны
			           |в процедуре %1
			           |общего модуля %2 по причине:
			           |
			           |%3'"),
			"ПриЗаполненииСписковСОграничениемДоступа",
			"УправлениеДоступомПереопределяемый",
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		ОбъектОшибки = "ОбщийМодуль.УправлениеДоступомПереопределяемый";
		Ошибки.Добавить(Новый Структура("ПолноеИмя, ТекстОшибки", ОбъектОшибки, ТекстОшибки));
		Возврат Ошибки;
	КонецПопытки;
	
	ТекстыОшибок = Новый Соответствие;
	ОбщийКонтекст.Вставить("СпискиСОграничением", СпискиСОграничением);
	ОбщийКонтекст.Вставить("ОписанияОграничений", Новый Соответствие);
	
	Для Каждого ОписаниеСписка Из ОбщийКонтекст.СпискиСОграничением Цикл
		ПолноеИмя = ОписаниеСписка.Ключ;
		Попытка
			ТекстОшибки = ОшибкаОграниченияДоступа(ОбщийКонтекст, ПолноеИмя);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ТекстОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		КонецПопытки;
		Если ЗначениеЗаполнено(ТекстОшибки) И ТекстыОшибок.Получить(ТекстОшибки) = Неопределено Тогда
			ТекстыОшибок.Вставить(ТекстОшибки, Истина);
			Ошибки.Добавить(Новый Структура("ПолноеИмя, ТекстОшибки", ПолноеИмя, ТекстОшибки));
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ошибки;
	
КонецФункции

// Возвращает настройки внедрения для инструментов разработчика.
//
// Параметры:
//  ДействующиеПараметры - Неопределено - значение по умолчанию.
//                       - Структура - только для вызова из функции РезультатПроверкиОграниченияДоступа.
//
// Возвращаемое значение:
//  Структура:
//    * ОграниченияВРолях - Структура:
//       ** ДляПользователей        - Соответствие из КлючИЗначение:
//            *** Ключ     - Строка - полное имя объекта метаданных (списка). Имя коллекции на английском.
//                                    В форме объекта должна вставка ПриЧтенииНаСервере.
//            *** Значение - Структура:
//                  **** ШаблонДляОбъекта - Булево - если Истина, тогда используется шаблон #ДляОбъекта,
//                                                   если Ложь,   тогда используется шаблон #ДляРегистра.
//                  **** Параметры - Массив из Строка - параметры шаблона (для объекта 1, для регистра 6).
//                                   Например, имя поля "Владелец" или опорного поля регистра "Организация".
//       ** ДляВнешнихПользователей - Соответствие из КлючИЗначение - как для пользователей выше:
//            *** Ключ     - Строка - полное имя объекта метаданных (списка). Имя коллекции на английском.
//                                    В форме объекта должна вставка ПриЧтенииНаСервере.
//            *** Значение - Структура:
//                  **** ШаблонДляОбъекта - Булево - если Истина, тогда используется шаблон #ДляОбъекта,
//                                                   если Ложь,   тогда используется шаблон #ДляРегистра.
//                  **** Параметры - Массив из Строка - параметры шаблона (для объекта 1, для регистра 6).
//                                   Например, имя поля "Владелец" или опорного поля регистра "Организация".
//
//    * ПредопределенныеИдентификаторы - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - имя требуемого предопределенного элемента справочника
//                               ИдентификаторыОбъектовМетаданных или ИдентификаторыОбъектовРасширений
//                               в формате "<ИмяСправочника>.<ИмяПредопределенного>".
//        ** Значение - Строка - полное имя соответствующего объекта метаданных.
//
//    * ВладельцыЗначенийКлючейДоступа - Структура - для группы определяемых типов:
//        ** Ссылки    - Массив из Строка - полные имена типов ссылки  (имя коллекции на английском).
//        ** Документы - Массив из Строка - полные имена типов объекта (имя коллекции на английском).
//        ** Объекты                      - Массив из Строка - то же, что в предыдущем пункте.
//        ** НаборыЗаписей                - Массив из Строка - то же, что в предыдущем пункте.
//        ** НаборыЗаписейРегистраРасчета - Массив из Строка - то же, что в предыдущем пункте.
//
//    * ЗначенияДоступа - Массив из Строка - полные имена типов ссылки (имя коллекции на английском).
//                                           Для дополнения определяемого типа ЗначениеДоступа.
//
//    * ТипыИзмеренийРегистровКлючей - Соответствие из КлючИЗначение:
//       Для измерений с именем Поле<?> регистра КлючиДоступаКРегистрам и регистров КлючиДоступаКРегистру<*>:
//           ** Ключ     - Строка - имя регистра.
//           ** Значение - Структура:
//               *** ИменаТипов - Массив из Строка - полные имена типов (на английском).
//               *** ПоляРегистров - Соответствие из КлючИЗначение:
//                     **** Ключ - Строка - полное имя регистра, который ограничивается.
//                     **** Значение - Массив из Структура:
//                            ***** Поле - Строка - имя поля регистра.
//                            ***** Тип  - ОписаниеТипов - типы поля регистра.
//                            Порядок полей в массиве соответствуют
//                            служебным полям Поле1, Поле2, ...
//               *** ПоляРегистровПоТипам - Соответствие из КлючИЗначение:
//                     **** Ключ - Строка - полное имя типа (на английском).
//                     **** Значение - Массив из Строка - полные имена полей, например,
//                                     "РегистрСведений.ДополнительныеСведения.Свойство".
//
Функция НастройкиВнедрения(ДействующиеПараметры = Неопределено) Экспорт
	
	ОграниченияВРолях = Новый Структура;
	ОграниченияВРолях.Вставить("ДляПользователей",        Новый Соответствие);
	ОграниченияВРолях.Вставить("ДляВнешнихПользователей", Новый Соответствие);
	
	ВладельцыЗначенийКлючейДоступа = Новый Структура;
	ВладельцыЗначенийКлючейДоступа.Вставить("Ссылки",        Новый Массив);
	ВладельцыЗначенийКлючейДоступа.Вставить("Документы",     Новый Массив);
	ВладельцыЗначенийКлючейДоступа.Вставить("Объекты",       Новый Массив);
	ВладельцыЗначенийКлючейДоступа.Вставить("НаборыЗаписей", Новый Массив);
	ВладельцыЗначенийКлючейДоступа.Вставить("НаборыЗаписейРегистраРасчета", Новый Массив);
	
	ПредопределенныеИдентификаторы = Новый Соответствие;
	ЗначенияДоступа = Новый Массив;
	ТипыИзмеренийРегистровКлючей = Новый Соответствие;
	ТипыИзмеренийРегистраКлючей(ТипыИзмеренийРегистровКлючей, "КлючиДоступаКРегистрам");
	
	ТипыТаблицПоИменам = УправлениеДоступомСлужебныйПовтИсп.СинтаксисЯзыка().ТипыТаблиц.ПоИменам;
	ЗначенияДоступа.Добавить(ИмяТипаСсылкиXML(Метаданные.Справочники.КлючиДоступа.ПолноеИмя(), ТипыТаблицПоИменам));
	
	ВозможныеПрава = ВозможныеПраваДляНастройкиПравОбъектов();
	Для Каждого ОписаниеВозможныхПрав Из ВозможныеПрава.ПоПолнымИменам Цикл
		ЗначенияДоступа.Добавить(ИмяТипаСсылкиXML(ОписаниеВозможныхПрав.Ключ, ТипыТаблицПоИменам));
	КонецЦикла;
	
	Если ДействующиеПараметры = Неопределено Тогда
		ОбщийКонтекст = ОбщийКонтекстРасчетаПараметровОграничения(, Истина);
		НовыеХранимыеПараметры = ХранимыеПараметрыОграниченияДоступа(ОбщийКонтекст);
		ДействующиеПараметры = Новый Структура(НовыеХранимыеПараметры.ДляЗаписиОбъектовИПроверкиПрав.Получить()); // см. НоваяСтруктураХранимыхПараметровЗаписи
	КонецЕсли;
	
	ДобавленныеСписки = Новый Соответствие;
	Для Каждого ОписаниеВерсии Из ДействующиеПараметры.ВерсииОграниченийСписков Цикл
		ПолноеИмя = ОписаниеВерсии.Ключ;
		ПолноеИмяXML = ПолноеИмяXML(ПолноеИмя, ТипыТаблицПоИменам);
		ИмяТипаСсылкиXML  = ИмяТипаСсылкиXML(ПолноеИмя, ТипыТаблицПоИменам);
		ИмяТипаОбъектаXML = ИмяТипаОбъектаИлиНабораЗаписейXML(ПолноеИмя, ТипыТаблицПоИменам);
		
		Если ЗначениеЗаполнено(ИмяТипаСсылкиXML) Тогда
			ВладельцыЗначенийКлючейДоступа.Ссылки.Добавить(ИмяТипаСсылкиXML);
		КонецЕсли;
		
		ДобавитьОбъектКВладельцам(ИмяТипаОбъектаXML, ВладельцыЗначенийКлючейДоступа);
		ДобавленныеСписки.Вставить(ВРег(ПолноеИмя), Истина);
		
		Если ОписаниеВерсии.Значение = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		ДобавитьОграниченияВРолях(ПолноеИмяXML,
			ПолноеИмя,
			ОграниченияВРолях.ДляПользователей,
			ДействующиеПараметры.ДополнительныйКонтекст.ДляПользователей.СвойстваОграниченияСписков,
			ТипыИзмеренийРегистровКлючей,
			ТипыТаблицПоИменам,
			ПредопределенныеИдентификаторы);
		
		ДобавитьОграниченияВРолях(ПолноеИмяXML,
			ПолноеИмя,
			ОграниченияВРолях.ДляВнешнихПользователей,
			ДействующиеПараметры.ДополнительныйКонтекст.ДляВнешнихПользователей.СвойстваОграниченияСписков,
			ТипыИзмеренийРегистровКлючей,
			ТипыТаблицПоИменам,
			ПредопределенныеИдентификаторы);
	КонецЦикла;
	
	Для Каждого ВедущийСписок Из ДействующиеПараметры.ВедущиеСписки Цикл
		ПолноеИмя = ВедущийСписок.Ключ;
		Если ДобавленныеСписки.Получить(ВРег(ПолноеИмя)) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ИмяТипаОбъектаXML = ИмяТипаОбъектаИлиНабораЗаписейXML(ПолноеИмя, ТипыТаблицПоИменам);
		ДобавитьОбъектКВладельцам(ИмяТипаОбъектаXML, ВладельцыЗначенийКлючейДоступа);
	КонецЦикла;
	
	Настройки = Новый Структура;
	Настройки.Вставить("ОграниченияВРолях",              ОграниченияВРолях);
	Настройки.Вставить("ПредопределенныеИдентификаторы", ПредопределенныеИдентификаторы);
	Настройки.Вставить("ВладельцыЗначенийКлючейДоступа", ВладельцыЗначенийКлючейДоступа);
	Настройки.Вставить("ЗначенияДоступа",                ЗначенияДоступа);
	Настройки.Вставить("ТипыИзмеренийРегистровКлючей",   ТипыИзмеренийРегистровКлючей);
	
	Возврат Настройки;
	
КонецФункции

// Возвращает результат проверки ограничения доступа для инструмента разработчика.
//
// Параметры:
//  ПолноеИмя - Строка - полное имя объекта метаданных.
//  ДополнительныеПараметры - Неопределено - проверить и вернуть текущее ограничение.
//                          - Структура:
//        * Текст - Строка - новый текст ограничения для пользователей.
//        * ТекстДляВнешнихПользователей - Строка - новый текст ограничения для внешних пользователей.
//        * УчитыватьЗависимости         - Булево - учитывать зависимости между ограничениями объектов.
//        * ВсеВидыДоступаИспользуются   - Неопределено - вычислить по текущим настройками.
//                                       - Булево - Истина (по умолчанию) - используются все,
//                                                  Ложь - не используется ни одного.
//
// Возвращаемое значение:
//  Структура:
//   * ОшибкаОписанияОграничения - Строка - если не пустая, то описание ограничения не удалось получить.
//       Если при этом УчитыватьЗависимости = Истина, то это текст первой ошибки при получении всех описаний.
//
//   * ТекстВМодулеМенеджера - Неопределено - когда заполнена ОшибкаОписанияОграничения.
//                           - Булево - возвращает место размещения текста ограничения,
//                               когда Истина - в модуле менеджера, иначе в переопределяемом модуле.
//
//   * ДляПользователей - Структура:
//      ** ПроверяемоеОграничение - Неопределено
//                                - Строка - проверенный текст ограничения.
//      ** ОписаниеОшибок         - см. ОписаниеОшибок
//      ** ОшибкаФормированияПараметровОграничения - Неопределено
//                                                 - Строка - текст ошибки.
//      ** ОшибкаФормированияТекстовЗапросов       - Неопределено
//                                                 - Строка - текст ошибки.
//      ** ОграничениеПоВладельцуВозможно     - Неопределено
//                                            - Булево - свойство проверенного ограничения.
//      ** ОграничениеПоВладельцуИспользуется - Неопределено
//                                            - Булево - когда УчитыватьЗависимости указано Истина.
//      ** ОграниченияВРолях - Неопределено
//                           - Структура:
//          *** ШаблонДляОбъекта - Булево - если Истина, тогда используется шаблон #ДляОбъекта,
//                                          если Ложь,   тогда используется шаблон #ДляРегистра.
//          *** Параметры - Массив - параметры шаблона (для объекта 1, для регистра 6):
//               **** Значение - Строка - например, имя поля "Владелец" или
//                                        опорного поля регистра "Организация".
//      ** ОграничениеВМодуле - Неопределено
//                            - Строка - текст ограничения, который установлен в конфигурации.
//      ** ПоВладельцуБезЗаписиКлючейДоступа - Неопределено
//                                           - Булево - настройка ограничения,
//                                               установленная в конфигурации.
//
//   * ДляВнешнихПользователей - Структура - со свойствами, как ДляПользователей.
//
//   * ВладелецЗначенийКлючейДоступа                            - Строка - типы для одноименного определяемого типа.
//   * ВладелецЗначенийКлючейДоступаОбъект                      - Строка - типы для одноименного определяемого типа.
//   * ВладелецЗначенийКлючейДоступаНаборЗаписей                - Строка - типы для одноименного определяемого типа.
//   * ВладелецЗначенийКлючейДоступаНаборЗаписейРегистраРасчета - Строка - типы для одноименного определяемого типа.
//   * ПолеРегистраКлючейДоступаКРегистрам                      - Строка - типы для одноименного определяемого типа.
//   * ЗначениеДоступа                                          - Строка - типы для одноименного определяемого типа.
//
//   * ТипыИзмеренийОтдельногоРегистраКлючей - Неопределено
//                                           - Структура:
//      ** ИмяРегистраСведений - Строка - имя регистра ключей доступа.
//      ** ТипыИзмерений       - Строка - типы для соответствующих измерений регистра.
//
//   * ПредопределенныйИдентификатор - Неопределено
//                                   - Структура:
//      ** ИмяСправочника       - Строка - имя справочника идентификаторы объектов метаданных (или расширений).
//      ** ИмяПредопределенного - Строка - имя предопределенного в справочнике.
//
Функция РезультатПроверкиОграниченияДоступа(ПолноеИмя, ДополнительныеПараметры = Неопределено) Экспорт
	
	Возврат РезультатПроверкиОграниченияДоступаОбъекта(ПолноеИмя, ДополнительныеПараметры);
	
КонецФункции

// Добавляет обновление доступа для указанных списков или всех списков.
// 
// Параметры:
//  Списки - Неопределено - запланировать полное обновление доступа.
//         - Строка - полное имя объекта метаданных.
//         - СправочникСсылка.ИдентификаторыОбъектовМетаданных - идентификатор.
//         - Массив
//         - ФиксированныйМассив - значения типов указанные выше, кроме Неопределено.
//
//  ПараметрыПланирования - см. ПараметрыПланированияОбновленияДоступа
//
Процедура ЗапланироватьОбновлениеДоступа(Списки = Неопределено, ПараметрыПланирования = Неопределено) Экспорт
	
	Если ПараметрыПланирования = Неопределено Тогда
		ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа();
	КонецЕсли;
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Если Не ПараметрыПланирования.КлючиДоступаКДанным
	   И Не ПараметрыПланирования.РазрешенныеКлючиДоступа Тогда
		
		Возврат;
	КонецЕсли;
	
	ИдентификаторыРегистров     = Новый Массив;
	ИдентификаторыСписковКлючей = Новый Массив;
	
	Если Списки = Неопределено Тогда
		Если ПараметрыПланирования.ВерсииОграниченийСписков = Неопределено Тогда
			ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь);
			ВерсииОграниченийСписков = Новый Соответствие(ДействующиеПараметры.ВерсииОграниченийСписков);
		Иначе
			ВерсииОграниченийСписков = ПараметрыПланирования.ВерсииОграниченийСписков;
		КонецЕсли;
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("РазрешенныйКлючДоступа",
			УправлениеДоступомСлужебныйПовтИсп.РазрешенныйКлючДоступа());
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ТИПЗНАЧЕНИЯ(КлючиДоступаКДанным.Объект) КАК ТипСсылки
		|ИЗ
		|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКДанным
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	КлючиДоступаКДанным.Регистр КАК Регистр
		|ИЗ
		|	РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКДанным
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	КлючиДоступа.Список КАК Список
		|ИЗ
		|	Справочник.КлючиДоступа КАК КлючиДоступа
		|ГДЕ
		|	КлючиДоступа.Ссылка <> &РазрешенныйКлючДоступа";
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
		ИдентификаторыРегистров     = РезультатыЗапроса[1].Выгрузить().ВыгрузитьКолонку("Регистр");
		ИдентификаторыСписковКлючей = РезультатыЗапроса[2].Выгрузить().ВыгрузитьКолонку("Список");
		
		Выборка = РезультатыЗапроса[0].Выбрать();
		ДопустимыеТипы = УправлениеДоступомСлужебныйПовтИсп.ОписаниеТиповСсылокДопустимыхОбъектов();
		ЕстьНедопустимыйТип = Ложь;
		Пока Выборка.Следующий() Цикл
			Если ТипЗнч(Выборка.ТипСсылки) <> Тип("Тип") Тогда
				Продолжить;
			ИначеЕсли Выборка.ТипСсылки = Тип("Неопределено")
			      Или Не ДопустимыеТипы.СодержитТип(Выборка.ТипСсылки) Тогда
				ЕстьНедопустимыйТип = Истина;
				Продолжить;
			КонецЕсли;
			ОбъектМетаданных = Метаданные.НайтиПоТипу(Выборка.ТипСсылки);
			Если ОбъектМетаданных = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ВерсииОграниченийСписков.Вставить(ОбъектМетаданных.ПолноеИмя(), Истина);
		КонецЦикла;
		ВерсииОграниченийСписков.Вставить("Справочник.НаборыГруппДоступа", Истина);
		Если ЕстьНедопустимыйТип Тогда
			ИдентификаторыРегистров.Добавить(Справочники.ИдентификаторыОбъектовМетаданных.ПустаяСсылка());
		КонецЕсли;
		
		СпискиДляОбновления = Новый Массив;
		Для Каждого КлючИЗначение Из ВерсииОграниченийСписков Цикл
			СпискиДляОбновления.Добавить(КлючИЗначение.Ключ);
		КонецЦикла;
		
		// При планировании полного обновления добавляются:
		// а) списки с ограничением,
		// б) списки, которые пишут ключи доступа для ограничений по полю-владельцу,
		// в) списки без ограничения, для которых есть записи в регистрах ключей доступа к данным,
		// в) списки, для которых рассчитываются разрешенные ключи доступа,
		// г) списки без расчета разрешенных ключей доступа, для которых есть записи в регистрах
		//    разрешенных ключей доступа.
		
	ИначеЕсли ТипЗнч(Списки) <> Тип("Массив")
	        И ТипЗнч(Списки) <> Тип("ФиксированныйМассив") Тогда
		
		СпискиДляОбновления = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Списки);
	Иначе
		СпискиДляОбновления = Списки;
	КонецЕсли;
	
	Если СпискиДляОбновления.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(СпискиДляОбновления[0]) = Тип("Строка") Тогда
		Идентификаторы = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(СпискиДляОбновления, Списки <> Неопределено);
		СпискиПоИдентификаторам = Новый Соответствие;
		Для Каждого ОписаниеИдентификатора Из Идентификаторы Цикл
			СпискиПоИдентификаторам.Вставить(ОписаниеИдентификатора.Значение, ОписаниеИдентификатора.Ключ);
		КонецЦикла;
	Иначе
		СпискиПоИдентификаторам = Новый Соответствие;
		Для Каждого Идентификатор Из СпискиДляОбновления Цикл
			СпискиПоИдентификаторам.Вставить(Идентификатор, "");
		КонецЦикла;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ИдентификаторыРегистров)
	 Или ЗначениеЗаполнено(ИдентификаторыСписковКлючей) Тогда
		
		Для Каждого ИдентификаторРегистра Из ИдентификаторыРегистров Цикл
			Если ИдентификаторРегистра = Неопределено Тогда
				СпискиПоИдентификаторам.Вставить(
					Справочники.ИдентификаторыОбъектовМетаданных.ПустаяСсылка(), "");
			КонецЕсли;
			СпискиПоИдентификаторам.Вставить(ИдентификаторРегистра, "");
		КонецЦикла;
		Для Каждого ИдентификаторРазрешенных Из ИдентификаторыСписковКлючей Цикл
			СпискиПоИдентификаторам.Вставить(ИдентификаторРазрешенных, "");
		КонецЦикла;
	КонецЕсли;
	
	ИдентификаторСправочникаНаборыГруппДоступа =
		СлужебныйИдентификатор("Справочник.НаборыГруппДоступа");
	
	ТекущаяДатаСеанса = ТекущаяДатаСеанса();
	КлючУникальностиЗаписей = Новый УникальныйИдентификатор;
	
	ЭтоТочечноеЗадание = Ложь;
	ПараметрыЗадания = Новый Структура;
	РазмерЗадания = 3;
	
	Если ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов Тогда
		ПараметрыПланирования.ЭтоПродолжениеОбновления = Истина;
		УстановитьВидКлючаДанных(ПараметрыЗадания, "УстаревшиеЭлементы");
		ДатаПоследнегоОбновленногоЭлемента = '00010101';
		РазмерЗадания = 2;
	Иначе
		ВедущийОбъект = ПараметрыПланирования.ВедущийОбъект; // См. ОписаниеВедущегоОбъекта
		Если ВедущийОбъект <> Неопределено
		   И Не (    ВедущийОбъект.Свойство("ПоДаннымКэшаРасчетаПрав")
		         Или ВедущийОбъект.Свойство("ПоКлючамДоступа")
		         Или ВедущийОбъект.Свойство("ПоЗначениямСГруппами")
		         Или ВедущийОбъект.Свойство("ПоЗначениямПолей")
		           И ТипЗнч(ВедущийОбъект.ПоЗначениямПолей.СоставИзменений) = Тип("ТаблицаЗначений") ) Тогда
			ВедущийОбъект = Неопределено;
		КонецЕсли;
		Если ВедущийОбъект <> Неопределено Тогда
			ЭтоТочечноеЗадание = Истина;
			ПараметрыПланирования = Новый Структура(Новый ФиксированнаяСтруктура(ПараметрыПланирования));
			ПараметрыПланирования.Вставить("ЭтоТочечноеЗадание");
			РазмерЗадания = 1;
			ПараметрыЗадания.Вставить("ТочечноеЗадание", ВедущийОбъект);
		КонецЕсли;
		ДатаПоследнегоОбновленногоЭлемента = ?(ПараметрыПланирования.ЭтоПродолжениеОбновления,
			МаксимальнаяДатаПриПродолжении(), МаксимальнаяДата());
	КонецЕсли;
	Если ПараметрыЗадания.Количество() = 0 Тогда
		ПараметрыЗадания = Неопределено;
	КонецЕсли;
	ХранилищеПараметровЗадания = Новый ХранилищеЗначения(ПараметрыЗадания);
	
	ОбновлениеКлючейДоступаКДанным       = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаКДанным);
	ОбновлениеКлючейДоступаПользователей = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаПользователей);
	
	Для Каждого ОписаниеСписка Из СпискиПоИдентификаторам Цикл
		ИдентификаторСписка = ОписаниеСписка.Ключ;
		
		Если ПараметрыПланирования.КлючиДоступаКДанным Тогда
			Если ПараметрыПланирования.ДляПользователей Тогда
				НоваяЗапись = ОбновлениеКлючейДоступаКДанным.Добавить();
				НоваяЗапись.КлючУникальности                   = КлючУникальностиЗаписей;
				НоваяЗапись.Список                             = ИдентификаторСписка;
				НоваяЗапись.ТочечноеЗадание                    = ЭтоТочечноеЗадание;
				НоваяЗапись.ДатаПоследнегоОбновленногоЭлемента = ДатаПоследнегоОбновленногоЭлемента;
				НоваяЗапись.ПараметрыЗадания                   = ХранилищеПараметровЗадания;
				НоваяЗапись.РазмерЗадания                      = РазмерЗадания;
				НоваяЗапись.ДатаИзмененияЗаписиРегистра        = ТекущаяДатаСеанса;
			КонецЕсли;
			Если ПараметрыПланирования.ДляВнешнихПользователей Тогда
				НоваяЗапись = ОбновлениеКлючейДоступаКДанным.Добавить();
				НоваяЗапись.КлючУникальности                   = КлючУникальностиЗаписей;
				НоваяЗапись.Список                             = ИдентификаторСписка;
				НоваяЗапись.ДляВнешнихПользователей            = Истина;
				НоваяЗапись.ТочечноеЗадание                    = ЭтоТочечноеЗадание;
				НоваяЗапись.ДатаПоследнегоОбновленногоЭлемента = ДатаПоследнегоОбновленногоЭлемента;
				НоваяЗапись.ПараметрыЗадания                   = ХранилищеПараметровЗадания;
				НоваяЗапись.РазмерЗадания                      = РазмерЗадания;
				НоваяЗапись.ДатаИзмененияЗаписиРегистра        = ТекущаяДатаСеанса;
			КонецЕсли;
		КонецЕсли;
		
		Если ИдентификаторСписка = ИдентификаторСправочникаНаборыГруппДоступа Тогда
			Продолжить;
		КонецЕсли;
		
		Если ПараметрыПланирования.РазрешенныеКлючиДоступа Тогда
			Если ПараметрыПланирования.ДляПользователей Тогда
				НоваяЗапись = ОбновлениеКлючейДоступаПользователей.Добавить();
				НоваяЗапись.КлючУникальности            = КлючУникальностиЗаписей;
				НоваяЗапись.Список                      = ИдентификаторСписка;
				НоваяЗапись.ТочечноеЗадание             = ЭтоТочечноеЗадание;
				НоваяЗапись.ПараметрыЗадания            = ХранилищеПараметровЗадания;
				НоваяЗапись.РазмерЗадания               = РазмерЗадания;
				НоваяЗапись.ДатаИзмененияЗаписиРегистра = ТекущаяДатаСеанса;
			КонецЕсли;
			Если ПараметрыПланирования.ДляВнешнихПользователей Тогда
				НоваяЗапись = ОбновлениеКлючейДоступаПользователей.Добавить();
				НоваяЗапись.КлючУникальности            = КлючУникальностиЗаписей;
				НоваяЗапись.Список                      = ИдентификаторСписка;
				НоваяЗапись.ДляВнешнихПользователей     = Истина;
				НоваяЗапись.ТочечноеЗадание             = ЭтоТочечноеЗадание;
				НоваяЗапись.ПараметрыЗадания            = ХранилищеПараметровЗадания;
				НоваяЗапись.РазмерЗадания               = РазмерЗадания;
				НоваяЗапись.ДатаИзмененияЗаписиРегистра = ТекущаяДатаСеанса;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Если ТранзакцияАктивна() И ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		ЗаблокироватьРегистрыПланированияОбновленияКлючейДоступаВФайловойИБ();
	КонецЕсли;
	
	Если ОбновлениеКлючейДоступаКДанным.Количество() > 0 Тогда
		ОбновлениеКлючейДоступаКДанным.Записать(Ложь);
	КонецЕсли;
	
	Если ОбновлениеКлючейДоступаПользователей.Количество() > 0 Тогда
		ОбновлениеКлючейДоступаПользователей.Записать(Ложь);
	КонецЕсли;
	
	ЗарегистрироватьПланированиеОбновленияДоступа(СпискиПоИдентификаторам,
		ПараметрыПланирования, Списки = Неопределено);
	
	Если ОбновлениеКлючейДоступаКДанным.Количество() > 0
	 Или ОбновлениеКлючейДоступаПользователей.Количество() > 0 Тогда
		
		УстановитьОбновлениеДоступа(Истина);
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Конструктор структуры дополнительных параметров для процедуры ЗапланироватьОбновлениеДоступа.
//
// Возвращаемое значение:
//  Структура:
//    * КлючиДоступаКДанным     - Булево - по умолчанию Истина - запланировать обновление ключей доступа
//                                  к данным.
//    * РазрешенныеКлючиДоступа - Булево - по умолчанию Истина - запланировать обновление ключей доступа
//                                  пользователей и групп доступа.
//    * ДляПользователей        - Булево - по умолчанию Истина - запланировать обновление для пользователей.
//    * ДляВнешнихПользователей - Булево - по умолчанию значение константы ИспользоватьВнешнихПользователей
//                                         запланировать обновление для внешних пользователей.
//
Функция ПараметрыПланированияОбновленияДоступа(ВычислитьДляВнешнихПользователей = Истина) Экспорт
	
	ДляВнешнихПользователей = ?(ВычислитьДляВнешнихПользователей,
		Константы.ИспользоватьВнешнихПользователей.Получить(), Истина);
	
	Результат = Новый Структура;
	Результат.Вставить("КлючиДоступаКДанным", Истина);
	Результат.Вставить("РазрешенныеКлючиДоступа", Истина);
	Результат.Вставить("ДляПользователей", Истина);
	Результат.Вставить("ДляВнешнихПользователей", ДляВнешнихПользователей);
	Результат.Вставить("ВедущийОбъект", Неопределено);
	Результат.Вставить("ВерсииОграниченийСписков", Неопределено);
	Результат.Вставить("Описание", "");
	Результат.Вставить("ЭтоПродолжениеОбновления", Ложь);
	Результат.Вставить("ЭтоОбработкаУстаревшихЭлементов", Ложь);
	
	Возврат Результат;
	
КонецФункции

// Параметры:
//  Описание - Строка   - имя процедуры, которая вызвала планирование,
//                        используемое для расширенной регистрации в журнале.
//  ЗапуститьОбновление - Булево - если Истина, то планирование будет
//                        негарантированным, так как при наличии транзакции
//                        выполняется в фоне "как есть" для возможности запуска обновления.
//
Процедура ЗапланироватьОбновлениеПараметровОграниченияДоступа(Описание, ЗапуститьОбновление = Ложь) Экспорт
	
	Если Не ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ТранзакцияАктивна() Или Не ЗапуститьОбновление Тогда
		ЗаписатьПланированиеОбновленияПараметровОграниченияДоступа(Описание, ЗапуститьОбновление);
		Возврат;
	КонецЕсли;
	
	ПараметрыПроцедуры = Новый Массив;
	ПараметрыПроцедуры.Добавить(Описание);
	ПараметрыПроцедуры.Добавить(ЗапуститьОбновление);
	
	ФоновыеЗадания.Выполнить(
		"УправлениеДоступомСлужебный.ЗаписатьПланированиеОбновленияПараметровОграниченияДоступа",
		ПараметрыПроцедуры,, НСтр("ru = 'Управление доступом: планирование обновления доступа'",
			ОбщегоНазначения.КодОсновногоЯзыка()));
	
КонецПроцедуры

// Только для процедуры ЗапланироватьОбновлениеПараметровОграниченияДоступа.
Процедура ЗаписатьПланированиеОбновленияПараметровОграниченияДоступа(Описание, ЗапуститьОбновление) Экспорт
	
	Если Не ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа();
	ПараметрыПланирования.РазрешенныеКлючиДоступа = Ложь;
	ПараметрыПланирования.Описание = Описание;
	
	Идентификатор = СлужебныйИдентификатор("РегистрСведений.ПараметрыОграниченияДоступа");
	Если Идентификатор <> Null Тогда
		ЗапланироватьОбновлениеДоступа(Идентификатор, ПараметрыПланирования);
		Если ЗапуститьОбновление Тогда
			ЗапуститьОбновлениеДоступа();
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Запускает обновление доступа, если оно запланировано и еще не запущено.
Процедура ЗапуститьОбновлениеДоступа() Экспорт
	
	Если Не ОграничиватьДоступНаУровнеЗаписейУниверсально(Ложь) Тогда
		Возврат;
	КонецЕсли;
	
	Если МонопольныйРежим() Тогда
		Возврат;
	КонецЕсли;
	
	ЗапуститьОбновлениеДоступаНаУровнеЗаписей();
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура ОбновитьДоступПослеОбновленияИнформационнойБазы(ВыполнитьОтложенноеОбновлениеСейчас) Экспорт
	
	Если ОбщегоНазначения.РазделениеВключено()
	   И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	Если ВыполнитьОтложенноеОбновлениеСейчас
	   И ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		
		ВыполнитьОбновлениеДоступаНаУровнеЗаписей(Истина, Ложь, 0, Истина);
	КонецЕсли;
	
КонецПроцедуры

// Для функции ПраваРолейРасширений и функции ЗаполнитьВсеПараметрыРаботыРасширений
// модуля менеджера регистра сведений ПараметрыРаботыВерсийРасширений.
//
Процедура УстановитьЗаписьПараметровОграниченияДоступаВТекущемСеансе(Включить) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	ТекущиеПараметры = ПараметрыСеанса.ПараметрыОграниченияДоступа;
	ИмяСвойства = "ЗаписьПараметровОграниченияДоступаВТекущемСеансе";
	
	Если Не Включить И Не ТекущиеПараметры.Свойство(ИмяСвойства) Тогда
		Возврат;
	КонецЕсли;
	
	НовыеПараметры = Новый Структура(ТекущиеПараметры);
	
	Если Включить Тогда
		Если Не НовыеПараметры.Свойство(ИмяСвойства) Тогда
			НовыеПараметры.Вставить(ИмяСвойства, 0);
		КонецЕсли;
		НовыеПараметры[ИмяСвойства] = НовыеПараметры[ИмяСвойства] + 1;
	Иначе
		НовыеПараметры[ИмяСвойства] = НовыеПараметры[ИмяСвойства] - 1;
		Если НовыеПараметры[ИмяСвойства] < 1 Тогда
			НовыеПараметры.Удалить(ИмяСвойства);
		КонецЕсли;
	КонецЕсли;
	
	ПараметрыСеанса.ПараметрыОграниченияДоступа = Новый ФиксированнаяСтруктура(НовыеПараметры);
	
	УстановитьПривилегированныйРежим(Ложь);
	
КонецПроцедуры

#КонецОбласти

////////////////////////////////////////////////////////////////////////////////
// Обработчики событий подсистем конфигурации.

// См. ГрупповоеИзменениеОбъектовПереопределяемый.ПриОпределенииОбъектовСРедактируемымиРеквизитами.
Процедура ПриОпределенииОбъектовСРедактируемымиРеквизитами(Объекты) Экспорт
	Объекты.Вставить(Метаданные.Справочники.ГруппыДоступа.ПолноеИмя(), "РеквизитыНеРедактируемыеВГрупповойОбработке");
	Объекты.Вставить(Метаданные.Справочники.ПрофилиГруппДоступа.ПолноеИмя(), "РеквизитыНеРедактируемыеВГрупповойОбработке");
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииОбработчиковУстановкиПараметровСеанса.
Процедура ПриДобавленииОбработчиковУстановкиПараметровСеанса(Обработчики) Экспорт
	
	Обработчики.Вставить("ОграничениеДоступаНаУровнеЗаписейИспользуется",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ВидыДоступаБезГруппДляЗначенияДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ВидыДоступаСОднойГруппойДляЗначенияДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ВидыДоступаСОтключеннымИспользованием",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ВсеВидыДоступаКромеСпециальных",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ТаблицыСОтдельнымиНастройкамиПрав",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ТипыЗначенийДоступаСГруппами",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ТипыВладельцевНастроекПрав",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ИдентификаторыТаблицСОтдельнымиНастройкамиПрав",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ТаблицыРасширенийСОграничениемДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	#Область УниверсальноеОграничение
	
	Обработчики.Вставить("ОграничениеДоступаНаУровнеЗаписейУниверсально",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ОтключениеОбновленияКлючейДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ВерсииШаблоновОграниченияДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("РазрешенныйНаборГруппДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("РазрешенныйПустойНаборГруппДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("РазрешенныйНаборГруппПользователей",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("РазрешенныйПользователь",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ОбщиеПараметрыШаблоновОграниченияДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("СпискиСОграничениемПоПолям",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("СпискиСОграничениемЧерезКлючиДоступаГруппДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("СпискиСОграничениемЧерезКлючиДоступаПользователей",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("СпискиСОтключеннымОграничениемЧтения",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	Обработчики.Вставить("ПараметрыОграниченияДоступа",
		"УправлениеДоступомСлужебный.УстановкаПараметровСеанса");
	
	#КонецОбласти
	
КонецПроцедуры

// См. РегламентныеЗаданияПереопределяемый.ПриОпределенииНастроекРегламентныхЗаданий
Процедура ПриОпределенииНастроекРегламентныхЗаданий(Настройки) Экспорт
	
	Настройка = Настройки.Добавить();
	Настройка.РегламентноеЗадание = Метаданные.РегламентныеЗадания.ЗаполнениеДанныхДляОграниченияДоступа;
	Настройка.ФункциональнаяОпция = Метаданные.ФункциональныеОпции.ОграничиватьДоступНаУровнеЗаписей;
	
	Настройка = Настройки.Добавить();
	Настройка.РегламентноеЗадание = Метаданные.РегламентныеЗадания.ОбновлениеДоступаНаУровнеЗаписей;
	Настройка.ФункциональнаяОпция = Метаданные.ФункциональныеОпции.ОграничиватьДоступНаУровнеЗаписейУниверсально;
	
КонецПроцедуры

// Обновляет вспомогательные данные, которые зависят только от конфигурации.
// Записывает изменения этих данных по версиям конфигурации(если изменения есть),
// чтобы использовать эти изменения при обновлении остальных вспомогательных данных,
// например, в обработчике ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации.
//
Процедура ОбновитьПараметрыОграниченияДоступа(ЕстьИзменения = Неопределено) Экспорт
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ПраваРолей".
	РегистрыСведений.ПраваРолей.ОбновитьДанныеРегистра(ЕстьИзменения);
	
	// ЗависимостиПравДоступа
	РегистрыСведений.ЗависимостиПравДоступа.ОбновитьДанныеРегистра(ЕстьИзменения);
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.СвойстваВидовДоступа".
	ОбновитьОписаниеСвойствВидовДоступа(ЕстьИзменения);
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ВозможныеПраваДляНастройкиПравОбъектов".
	РегистрыСведений.НастройкиПравОбъектов.ОбновитьВозможныеПраваДляНастройкиПравОбъектов(ЕстьИзменения);
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ОписаниеПоставляемыхПрофилей".
	Справочники.ПрофилиГруппДоступа.ОбновитьОписаниеПоставляемыхПрофилей(ЕстьИзменения);
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ПредопределенныеПрофилиГруппДоступа".
	Справочники.ПрофилиГруппДоступа.ОбновитьСоставПредопределенныхПрофилей(ЕстьИзменения);
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ВерсияТекстовОграниченияДоступа".
	РегистрыСведений.ПараметрыОграниченияДоступа.ОбновитьВерсиюТекстовОграниченияДоступа(ЕстьИзменения);
	
КонецПроцедуры

// Обновляет описание свойств видов доступа в параметрах работы программы.
// 
// Параметры:
//  ЕстьИзменения - Булево - возвращаемое значение. Если производилась запись,
//                  устанавливается Истина, иначе не изменяется.
//
Процедура ОбновитьОписаниеСвойствВидовДоступа(ЕстьИзменения = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса();
	НовоеЗначение = Кэш.ХешСуммы;
	
	НачатьТранзакцию();
	Попытка
		ЕстьТекущиеИзменения = Ложь;
		СтароеЗначение = Неопределено;
		
		СтандартныеПодсистемыСервер.ОбновитьПараметрРаботыПрограммы(
			"СтандартныеПодсистемы.УправлениеДоступом.СвойстваВидовДоступа",
			НовоеЗначение, ЕстьТекущиеИзменения, СтароеЗначение);
		
		СтароеЗначение = НовыеХешСуммыСвойствВидовДоступа(СтароеЗначение);
		
		ЕстьИзмененияТиповГруппИЗначенийДоступа =
			НовоеЗначение.ХешСуммаТиповГруппИЗначенийДоступа
			 <> СтароеЗначение.ХешСуммаТиповГруппИЗначенийДоступа;
		
		СтандартныеПодсистемыСервер.ДобавитьИзмененияПараметраРаботыПрограммы(
			"СтандартныеПодсистемы.УправлениеДоступом.ТипыГруппИЗначенийДоступа",
			?(ЕстьИзмененияТиповГруппИЗначенийДоступа,
			  Новый ФиксированнаяСтруктура("ЕстьИзменения", Истина),
			  Новый ФиксированнаяСтруктура()) );
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если ЕстьТекущиеИзменения Тогда
		ЕстьИзменения = Истина;
	КонецЕсли;
	
КонецПроцедуры

// См. ОбновлениеИнформационнойБазыБСП.ПриДобавленииОбработчиковОбновления.
Процедура ПриДобавленииОбработчиковОбновления(Обработчики) Экспорт
	
	// Обработчики обновления неразделенных данных.
	Обработчик = Обработчики.Добавить();
	Обработчик.ОбщиеДанные = Истина;
	Обработчик.УправлениеОбработчиками = Истина;
	Обработчик.Приоритет = 1;
	Обработчик.Версия = "*";
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.Процедура = "УправлениеДоступомСлужебный.ЗаполнитьОбработчикиРазделенныхДанных";
	
	// Обработчики обновления разделенных данных.
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "*";
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.Процедура = "УправлениеДоступомСлужебный.ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации";
	
	// Должен выполнятся после обработчика ЗаполнитьИдентификаторыПоставляемыхДанных.
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "1.0.0.1";
	Обработчик.НачальноеЗаполнение = Истина;
	Обработчик.Процедура = "Справочники.ГруппыДоступа.ЗаполнитьПрофильГруппыДоступаАдминистраторы";
	Обработчик.РежимВыполнения = "Монопольно";
	Обработчик.ВыполнятьВГруппеОбязательных = Истина;
	Обработчик.Приоритет = 1;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "2.4.1.1";
	Обработчик.Процедура = "УправлениеДоступомСлужебный.ОбновитьДанныеПрофиляОткрытиеВнешнихОтчетовИОбработок";
	Обработчик.РежимВыполнения = "Оперативно";
	Обработчик.ВыполнятьВГруппеОбязательных = Истина;
	Обработчик.Приоритет = 1;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.1.3.169";
	Обработчик.Процедура = "Справочники.ГруппыДоступа.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.ЗапускатьИВПодчиненномУзлеРИБСФильтрами = Истина;
	Обработчик.Комментарий = НСтр("ru = 'Устраняет некорректное скрытие данных в некоторых списках при включенном ограничении на уровне записей.'");
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("b73c2481-f789-4b2d-b705-8219aea0e75d");
	Обработчик.ПроцедураПроверки = "ОбновлениеИнформационнойБазы.ДанныеОбновленыНаНовуюВерсиюПрограммы";
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "Справочники.ГруппыДоступа.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ЧитаемыеОбъекты = "Справочник.ГруппыДоступа";
	Обработчик.ИзменяемыеОбъекты = "РегистрСведений.ТаблицыГруппДоступа,РегистрСведений.ЗначенияГруппДоступа,РегистрСведений.ЗначенияГруппДоступаПоУмолчанию";
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.Мультиязычность") Тогда
		Обработчик.ПриоритетыВыполнения = ОбновлениеИнформационнойБазы.ПриоритетыВыполненияОбработчика();
		НоваяСтрока = Обработчик.ПриоритетыВыполнения.Добавить();
		НоваяСтрока.Процедура = "МультиязычностьСервер.ОбработатьДанныеДляПереходаНаНовуюВерсию";
		НоваяСтрока.Порядок = "До";
	КонецЕсли;
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.1.3.135";
	Обработчик.Процедура = "РегистрыСведений.ГруппыЗначенийДоступа.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.Комментарий = НСтр("ru = 'Обновление данных по изменениям программы.'");
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("b3cb643e-d5cf-40b7-9db3-6315a88c063d");
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "РегистрыСведений.ГруппыЗначенийДоступа.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ЧитаемыеОбъекты = "РегистрСведений.ГруппыЗначенийДоступа";
	Обработчик.ИзменяемыеОбъекты = "РегистрСведений.ГруппыЗначенийДоступа";
	
	Обработчик = Обработчики.Добавить();
	Обработчик.Версия = "3.0.2.174";
	Обработчик.Процедура = "РегистрыСведений.НастройкиПравОбъектов.ОбработатьДанныеДляПереходаНаНовуюВерсию";
	Обработчик.РежимВыполнения = "Отложенно";
	Обработчик.Комментарий = НСтр("ru = 'Обновление вспомогательных данных настроек прав.'");
	Обработчик.Идентификатор = Новый УникальныйИдентификатор("40d1c62f-c3f1-4608-8985-2dc618c3d758");
	Обработчик.ПроцедураЗаполненияДанныхОбновления = "РегистрыСведений.НастройкиПравОбъектов.ЗарегистрироватьДанныеКОбработкеДляПереходаНаНовуюВерсию";
	Обработчик.ЧитаемыеОбъекты = "РегистрСведений.НастройкиПравОбъектов";
	Обработчик.ИзменяемыеОбъекты = "РегистрСведений.НастройкиПравОбъектов";
	
КонецПроцедуры

// Смотри также ОбновлениеИнформационнойБазыПереопределяемый.ПриОпределенииНастроек
//
// Параметры:
//  Объекты - Массив из ОбъектМетаданных
//
Процедура ПриОпределенииОбъектовСНачальнымЗаполнением(Объекты) Экспорт
	
	Объекты.Добавить(Метаданные.Справочники.ПрофилиГруппДоступа);
	Объекты.Добавить(Метаданные.Справочники.ГруппыДоступа);
	
КонецПроцедуры

// См. ОбновлениеИнформационнойБазыБСП.ПослеОбновленияИнформационнойБазы.
Процедура ПослеОбновленияИнформационнойБазы(Знач ПредыдущаяВерсия, Знач ТекущаяВерсия,
		Знач ВыполненныеОбработчики, ВыводитьОписаниеОбновлений, МонопольныйРежим) Экспорт
	
	ОбновитьВспомогательныеДанныеЭлементовИзмененныхПриЗагрузке();
	
	Если ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		РегистрыСведений.ИспользуемыеВидыДоступа.ОбновитьДанныеРегистра();
		УстановитьОбновлениеДоступа(Истина, Истина);
	КонецЕсли;
	
КонецПроцедуры

// См. ОбновлениеИнформационнойБазыПереопределяемый.ПриЗаполненииОбъектовПланируемыхКУдалению.
Процедура ПриЗаполненииОбъектовПланируемыхКУдалению(Объекты) Экспорт
	
	// РегистрСведений.ЗависимостиПравДоступа.ТипВедущейТаблицы
	ЗависимостиПравДоступа = РегистрыСведений.ЗависимостиПравДоступа.ЗависимостиПравДоступа();
	ТипыВедущихТаблиц = Новый Массив;
	Для Каждого Строка Из ЗависимостиПравДоступа Цикл
		ТипыВедущихТаблиц.Добавить(ТипЗнч(Строка.ТипВедущейТаблицы));
	КонецЦикла;
	ТребуемыйТипВедущейТаблицы = Новый ОписаниеТипов(ТипыВедущихТаблиц);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипВедущейТаблицы,
		Метаданные.РегистрыСведений.ЗависимостиПравДоступа.Измерения.ТипВедущейТаблицы);
	
	Если Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат;
	КонецЕсли;
	
	НастройкиВнедрения = НастройкиВнедрения();
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	
	// ОпределяемыйТип.ЗначениеДоступа
	ТипыГруппИЗначений = Новый Массив;
	Для Каждого КлючИЗначение Из СвойстваВидовДоступа.ПоТипамГруппИЗначений Цикл
		ТипыГруппИЗначений.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	ТребуемыйТипЗначенияДоступа = Новый ОписаниеТипов(
		СтрСоединить(НастройкиВнедрения.ЗначенияДоступа, ","));
	ТребуемыйТипЗначенияДоступа = Новый ОписаниеТипов(ТребуемыйТипЗначенияДоступа, ТипыГруппИЗначений);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипЗначенияДоступа,
		Метаданные.РегистрыСведений.ЗначенияГруппДоступа.Измерения.ЗначениеДоступа);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипЗначенияДоступа,
		Метаданные.РегистрыСведений.ЗначенияГруппДоступаПоУмолчанию.Измерения.ТипЗначенийДоступа);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипЗначенияДоступа,
		Метаданные.РегистрыСведений.ГруппыЗначенийДоступа.Измерения.ЗначениеДоступа);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипЗначенияДоступа,
		Метаданные.РегистрыСведений.ГруппыЗначенийДоступа.Измерения.ГруппаЗначенийДоступа);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипЗначенияДоступа,
		Метаданные.РегистрыСведений.ИспользуемыеВидыДоступа.Измерения.ТипЗначенийДоступа);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипЗначенияДоступа,
		Метаданные.РегистрыСведений.ИспользуемыеВидыДоступаПоТаблицам.Измерения.ТипЗначенийДоступа);
	
	// ОпределяемыйТип.ВладелецНастроекПрав
	ВозможныеПрава = ВозможныеПраваДляНастройкиПравОбъектов();
	ТипыВладельцевНастроекПрав = Новый Массив;
	Для Каждого ВладелецПрав Из ВозможныеПрава.ТипыВладельцев Цикл
		ТипыВладельцевНастроекПрав.Добавить(ТипЗнч(ВладелецПрав));
	КонецЦикла;
	ТребуемыйТипВладельцаНастроекПрав = Новый ОписаниеТипов(ТипыВладельцевНастроекПрав);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипВладельцаНастроекПрав,
		Метаданные.РегистрыСведений.НастройкиПравОбъектов.Измерения.Объект);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипВладельцаНастроекПрав,
		Метаданные.РегистрыСведений.НаследованиеНастроекПравОбъектов.Измерения.Объект);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипВладельцаНастроекПрав,
		Метаданные.РегистрыСведений.НаследованиеНастроекПравОбъектов.Измерения.Родитель);
	
	// ОпределяемыйТип.ВладелецЗначенийКлючейДоступа
	ДобавитьОбъектПланируемыйКУдалению(Объекты,
		Новый ОписаниеТипов(СтрСоединить(НастройкиВнедрения.ВладельцыЗначенийКлючейДоступа.Ссылки, ",")),
		Метаданные.РегистрыСведений.КлючиДоступаКОбъектам.Измерения.Объект);
	
	// ОпределяемыйТип.ПолеРегистраКлючейДоступаКРегистрам
	Для Каждого ОписаниеРегистровКлючей Из НастройкиВнедрения.ТипыИзмеренийРегистровКлючей Цикл
		ИмяРегистраКлючей = ОписаниеРегистровКлючей.Ключ;
		МетаданныеРегистраКлючей = Метаданные.РегистрыСведений[ИмяРегистраКлючей];
		КоличествоПолей = УправлениеДоступомСлужебныйПовтИсп.КоличествоОпорныхПолейРегистра(ИмяРегистраКлючей);
		ТребуемыйТипПоля = Новый ОписаниеТипов(СтрСоединить(ОписаниеРегистровКлючей.Значение.ИменаТипов, ","));
		Для НомерПоля = 1 По КоличествоПолей Цикл
			ИмяПоля = "Поле" + НомерПоля;
			ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипПоля,
				МетаданныеРегистраКлючей.Измерения[ИмяПоля]);
		КонецЦикла;
	КонецЦикла;
	
	// РегистрСведений.НаборыЗначенийДоступа.Объект
	ТипыОбъектовПодписок = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
		"ЗаписатьНаборыЗначенийДоступа");
	ТипыСсылокОбъектовПодписок = Новый Массив;
	Для Каждого КлючИЗначение Из ТипыОбъектовПодписок Цикл
		МетаданныеОбъекта = Метаданные.НайтиПоТипу(КлючИЗначение.Ключ);
		МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(МетаданныеОбъекта.ПолноеИмя());
		ПустаяСсылка = МенеджерОбъекта.ПустаяСсылка();
		ТипыСсылокОбъектовПодписок.Добавить(ТипЗнч(ПустаяСсылка));
	КонецЦикла;
	ТребуемыйТипТаблицСЗаписьюНаборовЗначенийДоступа = Новый ОписаниеТипов(ТипыСсылокОбъектовПодписок);
	
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипТаблицСЗаписьюНаборовЗначенийДоступа,
		Метаданные.РегистрыСведений.НаборыЗначенийДоступа.Измерения.Объект);
	
	// РегистрСведений.НаборыЗначенийДоступа.ЗначениеДоступа
	ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыйТипЗначенияДоступа,
		Метаданные.РегистрыСведений.НаборыЗначенийДоступа.Измерения.ЗначениеДоступа);
	
КонецПроцедуры

// Параметры:
//  ИзменениеЯзыков - см. МультиязычностьСервер.ОписаниеСтарыхИНовыхНастроекЯзыков
//
Процедура ПриИзмененииЯзыкаИнформационнойБазы(ИзменениеЯзыков) Экспорт
	
	Справочники.ПрофилиГруппДоступа.ПриИзмененииЯзыкаИнформационнойБазы(ИзменениеЯзыков);
	
КонецПроцедуры

// Заполняет структуру параметров, необходимых для работы клиентского кода
// конфигурации.
//
// Параметры:
//   Параметры   - Структура - структура параметров.
//
Процедура ПриДобавленииПараметровРаботыКлиента(Параметры) Экспорт
	
	Параметры.Вставить("УпрощенныйИнтерфейсНастройкиПравДоступа",
		УпрощенныйИнтерфейсНастройкиПравДоступа());
	
КонецПроцедуры

// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок.
Процедура ПриДобавленииИсключенийПоискаСсылок(ИсключенияПоискаСсылок) Экспорт
	
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ГруппыЗначенийДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ЗависимостиПравДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ЗначенияГруппДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ЗначенияГруппДоступаПоУмолчанию);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.НаборыЗначенийДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ПраваРолей);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.НаследованиеНастроекПравОбъектов);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.НастройкиПравОбъектов);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ТаблицыГруппДоступа);
	
	ПриДобавленииИсключенийПоискаСсылокДопускающихУдаление(ИсключенияПоискаСсылок);
	
КонецПроцедуры

// Объекты с отложенным удалением.
// См. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок
//
// Параметры:
//  ИсключенияПоискаСсылок - см. ОбщегоНазначенияПереопределяемый.ПриДобавленииИсключенийПоискаСсылок.ИсключенияПоискаСсылок
//
Процедура ПриДобавленииИсключенийПоискаСсылокДопускающихУдаление(ИсключенияПоискаСсылок) Экспорт
	
	ИсключенияПоискаСсылок.Добавить(Метаданные.Справочники.КлючиДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.Справочники.НаборыГруппДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.КлючиДоступаВнешнихПользователей);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.КлючиДоступаГруппДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.КлючиДоступаНаборовГруппДоступа);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.КлючиДоступаКОбъектам);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.КлючиДоступаКРегистрам);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.КлючиДоступаПользователей);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ОбновлениеКлючейДоступаКДанным);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ОбновлениеКлючейДоступаПользователей);
	ИсключенияПоискаСсылок.Добавить(Метаданные.РегистрыСведений.ПараметрыОграниченияДоступа);
	
	Для Каждого РегистрСведений Из Метаданные.РегистрыСведений Цикл 
		Если СтрНачинаетсяС(ВРег(РегистрСведений.Имя), ВРег("КлючиДоступаКРегистру")) Тогда 
			ИсключенияПоискаСсылок.Добавить(РегистрСведений);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Вызывается при загрузке ссылок предопределенных элементов в процессе загрузки важных данных.
// Позволяет выполнить действия по исправлению или регистрации сведений о не уникальности
// предопределенных элементов, а также позволяет отказаться от продолжения, если это недопустимо.
//
// Параметры:
//   Объект          - СправочникОбъект
//                   - ПланВидовХарактеристикОбъект
//                   - ПланСчетовОбъект
//                   - ПланВидовРасчетаОбъект -
//                     объект предопределенного элемента после записи которого обнаружено наличие не уникальности.
//   ЗаписатьВЖурнал - Булево - возвращаемое значение. Если указать Ложь, тогда сведения о не уникальности не будут
//                     добавлены в журнал регистрации в общем сообщении.
//                     Нужно установить Ложь, если не уникальность была устранена автоматически.
//   Отказ           - Булево - возвращаемое значение. Если указать Истина, будет вызвано общее исключение,
//                     содержащее все причины отказа.
//   ОписаниеОтказа  - Строка - возвращаемое значение. Если Отказ установлен в Истина, то описание будет добавлено
//                     в список причин невозможности продолжения.
//
Процедура ПриОбнаруженииНеУникальностиПредопределенного(Объект, ЗаписатьВЖурнал, Отказ, ОписаниеОтказа) Экспорт
	
	Если ТипЗнч(Объект) = Тип("СправочникОбъект.ПрофилиГруппДоступа")
	   И Объект.ИмяПредопределенныхДанных = "Администратор" Тогда
		
		ЗаписатьВЖурнал = Ложь;
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
		Запрос.УстановитьПараметр("ИмяПредопределенныхДанных", "Администратор");
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ПрофилиГруппДоступа.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
		|ГДЕ
		|	ПрофилиГруппДоступа.Ссылка <> &Ссылка
		|	И ПрофилиГруппДоступа.ИмяПредопределенныхДанных = &ИмяПредопределенныхДанных";
		
		Выборка = Запрос.Выполнить().Выбрать();
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
		
		Пока Выборка.Следующий() Цикл
			ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				ТекущийОбъект = Выборка.Ссылка.ПолучитьОбъект();
				ТекущийОбъект.ИмяПредопределенныхДанных = "";
				ТекущийОбъект.ИдентификаторПоставляемыхДанных = "";
				ОбновлениеИнформационнойБазы.ЗаписатьДанные(ТекущийОбъект);
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЦикла;
		
	ИначеЕсли ТипЗнч(Объект) = Тип("СправочникОбъект.ГруппыДоступа")
	        И Объект.ИмяПредопределенныхДанных = "Администраторы" Тогда
		
		ЗаписатьВЖурнал = Ложь;
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ИмяПредопределенныхДанных", "Администраторы");
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ГруппыДоступаПользователи.Пользователь
		|ИЗ
		|	Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
		|ГДЕ
		|	ГруппыДоступаПользователи.Ссылка.ИмяПредопределенныхДанных = &ИмяПредопределенныхДанных";
		ВсеПользователи = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Пользователь");
		
		Записать = Ложь;
		Для каждого Пользователь Из ВсеПользователи Цикл
			Если Объект.Пользователи.Найти(Пользователь, "Пользователь") = Неопределено Тогда
				Объект.Пользователи.Добавить().Пользователь = Пользователь;
				Записать = Истина;
			КонецЕсли;
		КонецЦикла;
		
		Если Записать Тогда
			ОбновлениеИнформационнойБазы.ЗаписатьДанные(Объект);
		КонецЕсли;
		
		Запрос.УстановитьПараметр("Ссылка", Объект.Ссылка);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ГруппыДоступа.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ГруппыДоступа КАК ГруппыДоступа
		|ГДЕ
		|	ГруппыДоступа.Ссылка <> &Ссылка
		|	И ГруппыДоступа.ИмяПредопределенныхДанных = &ИмяПредопределенныхДанных";
		
		Выборка = Запрос.Выполнить().Выбрать();
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
		
		Пока Выборка.Следующий() Цикл
			ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				ТекущийОбъект = Выборка.Ссылка.ПолучитьОбъект();
				ТекущийОбъект.ИмяПредопределенныхДанных = "";
				ОбновлениеИнформационнойБазы.ЗаписатьДанные(ТекущийОбъект);
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// См. ОбменДаннымиПереопределяемый.ПриНастройкеПодчиненногоУзлаРИБ.
Процедура ПриНастройкеПодчиненногоУзлаРИБ() Экспорт
	
	// Роли расширений назначаются независимо во всех РИБ-узлах.
	Справочники.ПрофилиГруппДоступа.УдалитьРолиРасширенийВоВсехПрофиляхГруппДоступа();
	
	// Администраторы назначаются независимо во всех РИБ-узлах.
	Справочники.ГруппыДоступа.УдалитьУчастниковГруппыДоступаАдминистраторыБезПользователяИБ();
	
	Если ОбщегоНазначения.ЭтоАвтономноеРабочееМесто() Тогда
		Справочники.ПрофилиГруппДоступа.ОбновитьПоставляемыеПрофилиПоИзменениямКонфигурации();
	КонецЕсли;
	Справочники.ПрофилиГруппДоступа.ОбновитьНепоставляемыеПрофилиПоИзменениямКонфигурации();
	
	Если ОграничиватьДоступНаУровнеЗаписейУниверсально()
	   И (ОбщегоНазначения.ЭтоПодчиненныйУзелРИБСФильтром()
	      Или ОбщегоНазначения.ЭтоАвтономноеРабочееМесто()) Тогда
		
		ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа();
		ПараметрыПланирования.Описание = "ПриНастройкеПодчиненногоУзлаРИБ";
		ЗапланироватьОбновлениеДоступа(, ПараметрыПланирования);
	КонецЕсли;
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхГлавному.
Процедура ПриОтправкеДанныхГлавному(ЭлементДанных, ОтправкаЭлемента, Получатель) Экспорт
	
	Если ОбъектПодсистемыУправлениеДоступомТолькоДляСозданияНачальногоОбраза(ЭлементДанных) Тогда
		ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать;
		Возврат;
	КонецЕсли;
	
	// Профиль и группа доступа открытия внешних отчетов и обработок
	// недоступны в сервисе, но доступны в автономном рабочем месте.
	Если ОбщегоНазначения.ЭтоАвтономноеРабочееМесто()
	   И (    ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ПрофилиГруппДоступа")
	        И ЭтоПрофильОткрытиеВнешнихОтчетовИОбработок(ЭлементДанных)
	      Или ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ГруппыДоступа")
	        И Не ЭлементДанных.ЭтоГруппа
	        И ЭтоПрофильОткрытиеВнешнихОтчетовИОбработок(ЭлементДанных.Профиль) ) Тогда
		
		ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать;
	КонецЕсли;
	
	// Роли расширений назначаются независимо во всех РИБ-узлах.
	Если ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ПрофилиГруппДоступа") Тогда
		Справочники.ПрофилиГруппДоступа.УдалитьРолиРасширений(ЭлементДанных);
	КонецЕсли;
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриОтправкеДанныхПодчиненному.
Процедура ПриОтправкеДанныхПодчиненному(ЭлементДанных, ОтправкаЭлемента, СозданиеНачальногоОбраза, Получатель) Экспорт
	
	// Роли расширений назначаются независимо во всех РИБ-узлах.
	// Частичное изменение объектов при создании начального образа не поддерживается,
	// удаление ролей расширений см. в процедуре ПриНастройкеПодчиненногоУзлаРИБ.
	
	// Администраторы назначаются независимо во всех РИБ-узлах.
	// Частичное изменение объектов при создании начального образа не поддерживается,
	// очистку состава администраторов см. в процедуре ПриНастройкеПодчиненногоУзлаРИБ.
	
	Если СозданиеНачальногоОбраза Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбъектПодсистемыУправлениеДоступомТолькоДляСозданияНачальногоОбраза(ЭлементДанных) Тогда
		ОтправкаЭлемента = ОтправкаЭлементаДанных.Игнорировать;
		Возврат;
	КонецЕсли;
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриПолученииДанныхОтГлавного.
Процедура ПриПолученииДанныхОтГлавного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Отправитель) Экспорт
	
	// Стандартная обработка не переопределяется.
	Если ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбъектПодсистемыУправлениеДоступомТолькоДляСозданияНачальногоОбраза(ЭлементДанных) Тогда
		ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;
		Возврат;
	КонецЕсли;
	
	ПриПолученииДанныхОтГлавногоИлиОтПодчиненного(ЭлементДанных);
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПриПолученииДанныхОтПодчиненного.
Процедура ПриПолученииДанныхОтПодчиненного(ЭлементДанных, ПолучениеЭлемента, ОтправкаНазад, Отправитель) Экспорт
	
	// Стандартная обработка не переопределяется.
	Если ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбъектПодсистемыУправлениеДоступомТолькоДляСозданияНачальногоОбраза(ЭлементДанных) Тогда
		ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;
		Возврат;
	КонецЕсли;
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		ПриПолученииДанныхОтГлавногоИлиОтПодчиненного(ЭлементДанных);
		
	ИначеЕсли ТипЗнч(ЭлементДанных) = Тип("КонстантаМенеджерЗначения.ОграничиватьДоступНаУровнеЗаписей")
	      Или ТипЗнч(ЭлементДанных) = Тип("КонстантаМенеджерЗначения.ОграничиватьДоступНаУровнеЗаписейУниверсально")
	      Или ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ГруппыДоступа")
	      Или ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ПрофилиГруппДоступа")
	      Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ГруппыЗначенийДоступа")
	      Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.НаборыЗначенийДоступа")
	      Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.НаследованиеНастроекПравОбъектов")
	      Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.НастройкиПравОбъектов")
	      Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ИспользуемыеВидыДоступа") Тогда
		
		// Получение данных из автономного рабочего места пропускается, а для соответствия
		// данных в узлах, текущие данные отправляются обратно в автономное рабочее место.
		ПолучениеЭлемента = ПолучениеЭлементаДанных.Игнорировать;
		ОтправкаНазад = Истина;
	КонецЕсли;
	
КонецПроцедуры

// См. СтандартныеПодсистемыСервер.ПослеПолученияДанных.
Процедура ПослеПолученияДанных(Отправитель, Отказ, ПолучениеИзГлавногоУзла) Экспорт
	
	Если ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы() Тогда
		Возврат;
	КонецЕсли;
	
	ОбновитьВспомогательныеДанныеЭлементовИзмененныхПриЗагрузке();
	
КонецПроцедуры

// См. описание в процедуре ЗаполнитьВсеПараметрыРаботыРасширений
// модуля менеджера регистра сведений ПараметрыРаботыВерсийРасширений.
//
Процедура ПриЗаполненииВсехПараметровРаботыРасширений() Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ПраваРолей".
	ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений();
	
	Если РегистрыСведений.ПараметрыРаботыПрограммы.НеобходимоОбновление() Тогда
		// Обновление выполняется в процедуре
		// ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации
		// или ПриНастройкеПодчиненногоУзлаРИБ.
		Возврат;
	КонецЕсли;
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.СвойстваВидовДоступа".
	ОбновитьГруппыИНаборыЗначенийДоступаПриИзмененииТиповГруппИЗначений();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ВозможныеПраваДляНастройкиПравОбъектов".
	РегистрыСведений.НастройкиПравОбъектов.ОбновитьВспомогательныеДанныеРегистраПоИзменениямКонфигурации();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ОписаниеПоставляемыхПрофилей".
	Справочники.ПрофилиГруппДоступа.ОбновитьПоставляемыеПрофилиПоИзменениямКонфигурации();
	Справочники.ПрофилиГруппДоступа.ОбновитьНепоставляемыеПрофилиПоИзменениямКонфигурации();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ПредопределенныеПрофилиГруппДоступа".
	Справочники.ГруппыДоступа.ПометитьНаУдалениеГруппыДоступаПомеченныхПрофилей();
	
	// Обновление ролей пользователей ИБ после таких изменений расширений,
	// которые приводят к рассогласованию ролей между пользователями ИБ и профилями,
	// но не меняют состав ролей профилей.
	ОбновитьРолиПользователей();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ВерсияТекстовОграниченияДоступа".
	ЗапланироватьОбновлениеПараметровОграниченияДоступа(
		"ПриЗаполненииВсехПараметровРаботыРасширений");
	
КонецПроцедуры

// Обработчики событий подсистемы Пользователи.

// См. ПользователиПереопределяемый.ПриОпределенииНастроек.
Процедура ПриОпределенииНастроек(Настройки) Экспорт
	
	// Роли устанавливаются автоматически по данным групп доступа
	// через связь: ПользователиГруппыДоступа -> Профиль -> РолиПрофиля.
	Настройки.РедактированиеРолей = Ложь;
	
КонецПроцедуры

// См. ПользователиПереопределяемый.ИзменитьДействияВФорме.
Процедура ПриОпределенииДействийВФорме(Знач ПользовательИлиГруппа, Знач ДействияВФорме) Экспорт
	
	ДействияВФорме.Роли = "";
	
КонецПроцедуры

// См. ИнтеграцияПодсистемБСП.ПослеЗаписиАдминистратораПриАвторизации.
Процедура ПослеЗаписиАдминистратораПриАвторизации(Комментарий) Экспорт
	
	Комментарий =
		НСтр("ru = 'Выполнен запуск от имени пользователя с ролью ""Полные права"",
		           |который не зарегистрирован в списке пользователей.
		           |Выполнена автоматическая регистрация в списке пользователей.
		           |Пользователь добавлен в группу доступа Администраторы.
		           |
		           |Для ведения списка и настройки прав пользователей предназначен список Пользователи,
		           |режим конфигурирования 1С:Предприятия для этого использовать не следует.'");
	
КонецПроцедуры

// См. ИнтеграцияПодсистемБСП.ПослеУстановкиПользователяИБ.
Процедура ПослеУстановкиПользователяИБ(Ссылка, ПарольПользователяСервиса) Экспорт
	
	ОбновитьРолиПользователей(Ссылка, ПарольПользователяСервиса);
	
	Если ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа(
			ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(Ссылка),, Истина);
	КонецЕсли;
	
КонецПроцедуры

// См. ИнтеграцияПодсистемБСП.ПриОпределенииТекстаВопросаПередЗаписьюПервогоАдминистратора.
Процедура ПриОпределенииТекстаВопросаПередЗаписьюПервогоАдминистратора(ТекстВопроса) Экспорт
	
	ТекстВопроса =
		НСтр("ru = 'В список пользователей программы добавляется первый пользователь,
		           |поэтому он будет автоматически включен в группу доступа Администраторы. 
		           |Продолжить?'")
	
КонецПроцедуры

// См. ИнтеграцияПодсистемБСП.ПриСозданииАдминистратора.
Процедура ПриСозданииАдминистратора(Администратор, Уточнение) Экспорт
	
	Если ТипЗнч(Администратор) <> Тип("СправочникСсылка.Пользователи") Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Пользователь", Администратор);
	Запрос.УстановитьПараметр("ГруппаДоступаАдминистраторы",
		УправлениеДоступом.ГруппаДоступаАдминистраторы());
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|ГДЕ
	|	ГруппыДоступаПользователи.Ссылка = &ГруппаДоступаАдминистраторы
	|	И ГруппыДоступаПользователи.Пользователь = &Пользователь";
	
	Если Не Запрос.Выполнить().Пустой() Тогда
		Возврат;
	КонецЕсли;
	
	КомментарийДляЖурнала = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Пользователь ""%1"" добавлен в группу доступа Администраторы по причине:
		           |%2'"),
		Администратор,
		Уточнение);
	
	ГруппаДоступаАдминистраторы = УправлениеДоступом.ГруппаДоступаАдминистраторы();
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
	ЭлементБлокировки.УстановитьЗначение("Ссылка", ГруппаДоступаАдминистраторы);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Объект = ГруппаДоступаАдминистраторы.ПолучитьОбъект();
		ЗаблокироватьДанныеДляРедактирования(Объект.Ссылка, Объект.ВерсияДанных);
		Если Объект.Пользователи.Найти(Администратор, "Пользователь") = Неопределено Тогда
			Объект.Пользователи.Добавить().Пользователь = Администратор;
			ОбновлениеИнформационнойБазы.ЗаписатьДанные(Объект);
			ЗаписьЖурналаРегистрации(
				НСтр("ru = 'Управление доступом.Автоматическое изменение группы доступа Администраторы'",
				     ОбщегоНазначения.КодОсновногоЯзыка()),
				УровеньЖурналаРегистрации.Информация,
				Метаданные.Справочники.Пользователи,
				Администратор,
				КомментарийДляЖурнала,
				РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
		КонецЕсли;
		РазблокироватьДанныеДляРедактирования(Объект.Ссылка);
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// См. ИнтеграцияПодсистемБСП.ПослеДобавленияИзмененияПользователяИлиГруппы.
Процедура ПослеДобавленияИзмененияПользователяИлиГруппы(Ссылка, ЭтоНовый) Экспорт
	
	Если ЭтоНовый Тогда
		Если ТипЗнч(Ссылка) = Тип("СправочникСсылка.ГруппыПользователей")
		 ИЛИ ТипЗнч(Ссылка) = Тип("СправочникСсылка.ГруппыВнешнихПользователей") Тогда
		
			Параметры = Новый Структура;
			Параметры.Вставить("ГруппыПользователей", Ссылка);
			РегистрыСведений.ГруппыЗначенийДоступа.ОбновитьГруппировкиПользователей(Параметры);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// См. ИнтеграцияПодсистемБСП.ПослеОбновленияСоставовГруппПользователей.
Процедура ПослеОбновленияСоставовГруппПользователей(УчастникиИзменений, ИзмененныеГруппы) Экспорт
	
	Параметры = Новый Структура;
	Параметры.Вставить("Пользователи",        УчастникиИзменений);
	Параметры.Вставить("ГруппыПользователей", ИзмененныеГруппы);
	
	РегистрыСведений.ГруппыЗначенийДоступа.ОбновитьГруппировкиПользователей(Параметры);
	
	ОбновитьРолиПользователей(УчастникиИзменений);
	
	Если ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа(УчастникиИзменений);
		ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа(ИзмененныеГруппы);
	КонецЕсли;
	
КонецПроцедуры

// См. ИнтеграцияПодсистемБСП.ПослеИзмененияОбъектаАвторизацииВнешнегоПользователя.
Процедура ПослеИзмененияОбъектаАвторизацииВнешнегоПользователя(ВнешнийПользователь,
                                                               СтарыйОбъектАвторизации,
                                                               НовыйОбъектАвторизации) Экспорт
	
	ОбъектыАвторизации = Новый Массив;
	Если СтарыйОбъектАвторизации <> NULL Тогда
		ОбъектыАвторизации.Добавить(СтарыйОбъектАвторизации);
	КонецЕсли;
	ОбъектыАвторизации.Добавить(НовыйОбъектАвторизации);
	
	Параметры = Новый Структура;
	Параметры.Вставить("ОбъектыАвторизации", ОбъектыАвторизации);
	
	РегистрыСведений.ГруппыЗначенийДоступа.ОбновитьГруппировкиПользователей(Параметры);
	
КонецПроцедуры

// Выполняет копирование прав от одного пользователя другому.
Процедура ПриКопированииПравНовомуПользователю(Источник, Приемник) Экспорт
	
	Если ТранзакцияАктивна()
	   И ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		
		Блокировка = Новый БлокировкаДанных;
		Блокировка.Добавить("Справочник.ГруппыДоступа");
		// АПК:1320-выкл - №499, №783.1.3 Допустимо вызывать блокировку во внешней транзакции для файловой ИБ.
		// Требуется для предотвращения взаимоблокировки: ниже запрос, который ставит неявную
		// разделяемую блокировку и далее блокировка усиливается до исключительной явным вызовом,
		// что приводит к взаимоблокировке в некоторых случаях.
		Блокировка.Заблокировать();
		// АПК:1320-вкл.
	КонецЕсли;
	
	УпрощенныйИнтерфейс = УпрощенныйИнтерфейсНастройкиПравДоступа();
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Пользователь", Источник);
	
	Если УпрощенныйИнтерфейс Тогда
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ГруппыДоступа.Профиль КАК Профиль
		|ИЗ
		|	Справочник.ГруппыДоступа КАК ГруппыДоступа
		|	ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
		|	ПО
		|		ГруппыДоступа.Пользователь = &Пользователь
		|		И ГруппыДоступаПользователи.Ссылка = ГруппыДоступа.Ссылка
		|		И ГруппыДоступаПользователи.Пользователь = &Пользователь";
	Иначе
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ГруппыДоступаПользователи.Ссылка КАК ГруппаДоступа
		|ИЗ
		|	Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
		|ГДЕ
		|	ГруппыДоступаПользователи.Пользователь = &Пользователь";
	КонецЕсли;
	
	РезультатЗапроса = Запрос.Выполнить();
	Если РезультатЗапроса.Пустой() Тогда
		Возврат;
	КонецЕсли;
	
	Выборка = РезультатЗапроса.Выбрать();
	
	Если Не УпрощенныйИнтерфейс Тогда
		Блокировка = Новый БлокировкаДанных();
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
		ЭлементБлокировки.ИсточникДанных = РезультатЗапроса;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Если УпрощенныйИнтерфейс Тогда
			Пока Выборка.Следующий() Цикл
				УправлениеДоступом.ВключитьОтключитьПрофильПользователя(Приемник, Выборка.Профиль, Истина, Источник);
			КонецЦикла;
		Иначе
			Блокировка.Заблокировать();
			Пока Выборка.Следующий() Цикл
				ГруппаДоступаОбъект = Выборка.ГруппаДоступа.ПолучитьОбъект(); // СправочникОбъект.ГруппыДоступа
				Если ГруппаДоступаОбъект.Пользователи.Найти(Приемник, "Пользователь") = Неопределено Тогда
					Строка = ГруппаДоступаОбъект.Пользователи.Добавить();
					Строка.Пользователь = Приемник;
					ГруппаДоступаОбъект.Записать();
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Обработчики событий подсистемы ВариантыОтчетов.

// См. ВариантыОтчетовПереопределяемый.НастроитьВариантыОтчетов.
Процедура ПриНастройкеВариантовОтчетов(Настройки) Экспорт
	
	МодульВариантыОтчетов = ОбщегоНазначения.ОбщийМодуль("ВариантыОтчетов");
	МодульВариантыОтчетов.НастроитьОтчетВМодулеМенеджера(Настройки, Метаданные.Отчеты.ПраваДоступа);
	МодульВариантыОтчетов.НастроитьОтчетВМодулеМенеджера(Настройки, Метаданные.Отчеты.АнализПравДоступа);
	МодульВариантыОтчетов.НастроитьОтчетВМодулеМенеджера(Настройки, Метаданные.Отчеты.ПраваРолей);
	
КонецПроцедуры

// См. ВариантыОтчетовПереопределяемый.ПередДобавлениемКомандОтчетов.
Процедура ПередДобавлениемКомандОтчетов(КомандыОтчетов, Параметры, СтандартнаяОбработка) Экспорт
	
	Отчеты.АнализПравДоступа.ПередДобавлениемКомандОтчетов(КомандыОтчетов, Параметры, СтандартнаяОбработка);
	Отчеты.ПраваРолей.ПередДобавлениемКомандОтчетов(КомандыОтчетов, Параметры, СтандартнаяОбработка);
	
КонецПроцедуры

// Обработчики событий подсистемы РассылкаОтчетов.

// См. РассылкаОтчетовПереопределяемый.ОпределитьИсключаемыеОтчеты
Процедура ПриОпределенииИсключаемыхОтчетов(ИсключаемыеОтчеты) Экспорт
	
	ИсключаемыеОтчеты.Добавить(Метаданные.Отчеты.ПраваДоступа);
	
КонецПроцедуры

// Обработчики событий подсистемы КонтрольВеденияУчета.

// См. ИнтеграцияПодсистемБСП.ПриОпределенииОбъектовИсключаемыхИзПроверки
Процедура ПриОпределенииОбъектовИсключаемыхИзПроверки(Объекты) Экспорт
	ПриДобавленииИсключенийПоискаСсылокДопускающихУдаление(Объекты);
КонецПроцедуры

// Обработчики событий подсистемы УправлениеДоступом.

// См. УправлениеДоступомПереопределяемый.ПриЗаполненииСписковСОграничениемДоступа.
Процедура ПриЗаполненииСписковСОграничениемДоступа(Списки) Экспорт
	
	Списки.Вставить(Метаданные.Справочники.ПрофилиГруппДоступа, Истина);
	Списки.Вставить(Метаданные.Справочники.ГруппыДоступа, Истина);
	
КонецПроцедуры

// Обработчики событий библиотеки ТехнологияСервиса.

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПриРегистрацииОбработчиковВыгрузкиДанных
Процедура ПриРегистрацииОбработчиковВыгрузкиДанных(ТаблицаОбработчиков) Экспорт
	
	Обработчик = ТаблицаОбработчиков.Добавить();
	Обработчик.ОбъектМетаданных = Метаданные.Справочники.ПрофилиГруппДоступа;
	Обработчик.Обработчик = Справочники.ПрофилиГруппДоступа;
	Обработчик.ПередВыгрузкойОбъекта = Истина;
	Обработчик.Версия = "1.0.0.1";
	
	Обработчик = ТаблицаОбработчиков.Добавить();
	Обработчик.ОбъектМетаданных = Метаданные.Справочники.ГруппыДоступа;
	Обработчик.Обработчик = Справочники.ГруппыДоступа;
	Обработчик.ПередВыгрузкойОбъекта = Истина;
	Обработчик.Версия = "1.0.0.1";
	
	Обработчик = ТаблицаОбработчиков.Добавить();
	Обработчик.ОбъектМетаданных = Метаданные.РегистрыСведений.ЗначенияГруппДоступа;
	Обработчик.Обработчик = РегистрыСведений.ЗначенияГруппДоступа;
	Обработчик.ПередВыгрузкойОбъекта = Истина;
	Обработчик.Версия = "1.0.0.1";
	
	Обработчик = ТаблицаОбработчиков.Добавить();
	Обработчик.ОбъектМетаданных = Метаданные.РегистрыСведений.ЗначенияГруппДоступаПоУмолчанию;
	Обработчик.Обработчик = РегистрыСведений.ЗначенияГруппДоступаПоУмолчанию;
	Обработчик.ПередВыгрузкойОбъекта = Истина;
	Обработчик.Версия = "1.0.0.1";
	
КонецПроцедуры

// Процедуры и функции подсистемы ЦентрМониторинга.

// Возвращает текст запроса для сбора статистической информации об использовании профилей групп доступа и ролей.
//
// Возвращаемое значение:
//  Строка
//
Функция ТекстЗапросаИспользованияРолей() Экспорт
	
	Возврат
	"ВЫБРАТЬ
	|	СоставыГруппПользователей.ГруппаПользователей КАК ГруппаПользователей,
	|	СведенияОПользователях.Пользователь КАК Пользователь,
	|	ВЫБОР
	|		КОГДА РАЗНОСТЬДАТ(СведенияОПользователях.ДатаПоследнейАктивности, &ТекущаяДата, ДЕНЬ) <= 7
	|			ТОГДА 1
	|		ИНАЧЕ 0
	|	КОНЕЦ КАК АктивныхЗаНеделю,
	|	ВЫБОР
	|		КОГДА РАЗНОСТЬДАТ(СведенияОПользователях.ДатаПоследнейАктивности, &ТекущаяДата, ДЕНЬ) <= 30
	|			ТОГДА 1
	|		ИНАЧЕ 0
	|	КОНЕЦ КАК АктивныхЗаМесяц
	|ПОМЕСТИТЬ ВТСоставыГруппИАктивность
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.СведенияОПользователях КАК СведенияОПользователях
	|		ПО СоставыГруппПользователей.Пользователь = СведенияОПользователях.Пользователь
	|ГДЕ
	|	СоставыГруппПользователей.Используется
	|	И НЕ СоставыГруппПользователей.Пользователь.ИдентификаторПользователяИБ = &ПустойУИД
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	ГруппаПользователей,
	|	Пользователь
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ПрофилиГруппДоступа.Ссылка КАК Профиль,
	|	ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных КАК ИдентификаторПоставляемыхДанных,
	|	ПрофилиГруппДоступа.ПоставляемыйПрофильИзменен КАК ПоставляемыйПрофильИзменен,
	|	ПрофилиГруппДоступа.Наименование КАК Наименование,
	|	ГруппыДоступаПользователи.Ссылка КАК ГруппаДоступа,
	|	ГруппыДоступаПользователи.Ссылка.Пользователь = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
	|		ИЛИ ГруппыДоступаПользователи.Ссылка.Пользователь = ЗНАЧЕНИЕ(Справочник.ВнешниеПользователи.ПустаяСсылка)
	|		ИЛИ ГруппыДоступаПользователи.Ссылка.Пользователь = НЕОПРЕДЕЛЕНО КАК ОбщаяГруппаДоступа,
	|	ГруппыДоступаПользователи.Пользователь КАК ПользовательТЧ,
	|	СоставыГруппПользователей.Пользователь КАК ПользовательРС,
	|	СоставыГруппПользователей.АктивныхЗаНеделю КАК АктивныхЗаНеделю,
	|	СоставыГруппПользователей.АктивныхЗаМесяц КАК АктивныхЗаМесяц
	|ПОМЕСТИТЬ ВТДанныеПрофилей
	|ИЗ
	|	Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТСоставыГруппИАктивность КАК СоставыГруппПользователей
	|		ПО ГруппыДоступаПользователи.Пользователь = СоставыГруппПользователей.ГруппаПользователей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|		ПО (ПрофилиГруппДоступа.Ссылка = ГруппыДоступаПользователи.Ссылка.Профиль)
	|			И (НЕ ГруппыДоступаПользователи.Ссылка.Профиль.ПометкаУдаления)
	|ГДЕ
	|	НЕ ГруппыДоступаПользователи.Ссылка.Профиль.ПометкаУдаления
	|
	|СГРУППИРОВАТЬ ПО
	|	ПрофилиГруппДоступа.Ссылка,
	|	ПрофилиГруппДоступа.Наименование,
	|	ГруппыДоступаПользователи.Пользователь,
	|	СоставыГруппПользователей.Пользователь,
	|	СоставыГруппПользователей.АктивныхЗаНеделю,
	|	СоставыГруппПользователей.АктивныхЗаМесяц,
	|	ГруппыДоступаПользователи.Ссылка,
	|	ГруппыДоступаПользователи.Ссылка.Пользователь = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
	|		ИЛИ ГруппыДоступаПользователи.Ссылка.Пользователь = ЗНАЧЕНИЕ(Справочник.ВнешниеПользователи.ПустаяСсылка)
	|		ИЛИ ГруппыДоступаПользователи.Ссылка.Пользователь = НЕОПРЕДЕЛЕНО,
	|	ПрофилиГруппДоступа.ИдентификаторПоставляемыхДанных,
	|	ПрофилиГруппДоступа.ПоставляемыйПрофильИзменен
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ВТДанныеПрофилей.Профиль КАК Профиль,
	|	ВТДанныеПрофилей.Наименование КАК Наименование,
	|	ВТДанныеПрофилей.ИдентификаторПоставляемыхДанных КАК ИдентификаторПоставляемыхДанных,
	|	ВТДанныеПрофилей.ПоставляемыйПрофильИзменен КАК ПоставляемыйПрофильИзменен
	|ПОМЕСТИТЬ Профили
	|ИЗ
	|	ВТДанныеПрофилей КАК ВТДанныеПрофилей
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Профиль
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Профили.Профиль КАК Профиль,
	|	СУММА(ВЫБОР
	|			КОГДА НЕ ПрофилиГруппДоступаВидыДоступа.ВидДоступа ЕСТЬ NULL
	|				ТОГДА 1
	|			ИНАЧЕ 0
	|		КОНЕЦ) КАК ВсегоВидовДоступа,
	|	СУММА(ВЫБОР
	|			КОГДА ЕСТЬNULL(ПрофилиГруппДоступаВидыДоступа.Предустановленный, ЛОЖЬ)
	|				ТОГДА 1
	|			ИНАЧЕ 0
	|		КОНЕЦ) КАК ПредустановленныхВидовДоступа
	|ПОМЕСТИТЬ ВидыДоступа
	|ИЗ
	|	Профили КАК Профили
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.ВидыДоступа КАК ПрофилиГруппДоступаВидыДоступа
	|		ПО Профили.Профиль = ПрофилиГруппДоступаВидыДоступа.Ссылка
	|
	|СГРУППИРОВАТЬ ПО
	|	Профили.Профиль
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Вложенный.Профиль КАК Профиль,
	|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Вложенный.ГруппаДоступа) КАК ГруппаДоступа,
	|	СУММА(ВЫБОР
	|			КОГДА НЕ Вложенный.ОбщаяГруппаДоступа
	|				ТОГДА 1
	|			ИНАЧЕ 0
	|		КОНЕЦ) КАК ПерсональнаяГруппа
	|ПОМЕСТИТЬ ГруппыДоступа
	|ИЗ
	|	(ВЫБРАТЬ
	|		ВТДанныеПрофилей.Профиль КАК Профиль,
	|		ВТДанныеПрофилей.ГруппаДоступа КАК ГруппаДоступа,
	|		ВТДанныеПрофилей.ОбщаяГруппаДоступа КАК ОбщаяГруппаДоступа
	|	ИЗ
	|		ВТДанныеПрофилей КАК ВТДанныеПрофилей
	|	
	|	СГРУППИРОВАТЬ ПО
	|		ВТДанныеПрофилей.Профиль,
	|		ВТДанныеПрофилей.ГруппаДоступа,
	|		ВТДанныеПрофилей.ОбщаяГруппаДоступа) КАК Вложенный
	|
	|СГРУППИРОВАТЬ ПО
	|	Вложенный.Профиль
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Профиль
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Вложенный.Профиль КАК Профиль,
	|	СУММА(ВЫБОР
	|			КОГДА Вложенный.ПользовательТЧ ССЫЛКА Справочник.ГруппыПользователей
	|				ТОГДА 1
	|			ИНАЧЕ 0
	|		КОНЕЦ) КАК ГруппПользователей,
	|	СУММА(ВЫБОР
	|			КОГДА Вложенный.ПользовательТЧ ССЫЛКА Справочник.ГруппыВнешнихПользователей
	|				ТОГДА 1
	|			ИНАЧЕ 0
	|		КОНЕЦ) КАК ГруппВнешнихПользователей
	|ПОМЕСТИТЬ ГруппыПользователей
	|ИЗ
	|	(ВЫБРАТЬ
	|		ВТДанныеПрофилей.Профиль КАК Профиль,
	|		ВТДанныеПрофилей.ПользовательТЧ КАК ПользовательТЧ
	|	ИЗ
	|		ВТДанныеПрофилей КАК ВТДанныеПрофилей
	|	
	|	СГРУППИРОВАТЬ ПО
	|		ВТДанныеПрофилей.Профиль,
	|		ВТДанныеПрофилей.ПользовательТЧ) КАК Вложенный
	|
	|СГРУППИРОВАТЬ ПО
	|	Вложенный.Профиль
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Профиль
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Вложенный.Профиль КАК Профиль,
	|	СУММА(ВЫБОР
	|			КОГДА Вложенный.ПользовательРС ССЫЛКА Справочник.Пользователи
	|				ТОГДА 1
	|			ИНАЧЕ 0
	|		КОНЕЦ) КАК Пользователи,
	|	СУММА(ВЫБОР
	|			КОГДА Вложенный.ПользовательРС ССЫЛКА Справочник.ВнешниеПользователи
	|				ТОГДА 1
	|			ИНАЧЕ 0
	|		КОНЕЦ) КАК ВнешниеПользователи,
	|	СУММА(Вложенный.АктивныхЗаНеделю) КАК АктивныхЗаНеделю,
	|	СУММА(Вложенный.АктивныхЗаМесяц) КАК АктивныхЗаМесяц
	|ПОМЕСТИТЬ ПользователиПрофиля
	|ИЗ
	|	(ВЫБРАТЬ
	|		ВТДанныеПрофилей.Профиль КАК Профиль,
	|		ВТДанныеПрофилей.ПользовательРС КАК ПользовательРС,
	|		СУММА(ВТДанныеПрофилей.АктивныхЗаНеделю) КАК АктивныхЗаНеделю,
	|		СУММА(ВТДанныеПрофилей.АктивныхЗаМесяц) КАК АктивныхЗаМесяц
	|	ИЗ
	|		ВТДанныеПрофилей КАК ВТДанныеПрофилей
	|	
	|	СГРУППИРОВАТЬ ПО
	|		ВТДанныеПрофилей.Профиль,
	|		ВТДанныеПрофилей.ПользовательРС) КАК Вложенный
	|
	|СГРУППИРОВАТЬ ПО
	|	Вложенный.Профиль
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Профиль
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	Профили.Профиль КАК Профиль,
	|	Профили.Наименование КАК Наименование,
	|	Профили.ИдентификаторПоставляемыхДанных КАК ИдентификаторПоставляемыхДанных,
	|	Профили.ПоставляемыйПрофильИзменен КАК ПоставляемыйПрофильИзменен,
	|	ЕСТЬNULL(ГруппыДоступа.ГруппаДоступа, 0) КАК ГруппаДоступа,
	|	ЕСТЬNULL(ГруппыДоступа.ПерсональнаяГруппа, 0) КАК ПерсональнаяГруппа,
	|	ЕСТЬNULL(ГруппыПользователей.ГруппПользователей, 0) КАК ГруппПользователей,
	|	ЕСТЬNULL(ГруппыПользователей.ГруппВнешнихПользователей, 0) КАК ГруппВнешнихПользователей,
	|	ЕСТЬNULL(ПользователиПрофиля.Пользователи, 0) КАК Пользователи,
	|	ЕСТЬNULL(ПользователиПрофиля.ВнешниеПользователи, 0) КАК ВнешниеПользователи,
	|	ЕСТЬNULL(ПользователиПрофиля.АктивныхЗаНеделю, 0) КАК АктивныхЗаНеделю,
	|	ЕСТЬNULL(ПользователиПрофиля.АктивныхЗаМесяц, 0) КАК АктивныхЗаМесяц,
	|	ЕСТЬNULL(ВидыДоступа.ВсегоВидовДоступа, 0) КАК ВсегоВидовДоступа,
	|	ЕСТЬNULL(ВидыДоступа.ПредустановленныхВидовДоступа, 0) КАК ПредустановленныхВидовДоступа
	|ИЗ
	|	Профили КАК Профили
	|		ЛЕВОЕ СОЕДИНЕНИЕ ПользователиПрофиля КАК ПользователиПрофиля
	|		ПО Профили.Профиль = ПользователиПрофиля.Профиль
	|		ЛЕВОЕ СОЕДИНЕНИЕ ГруппыПользователей КАК ГруппыПользователей
	|		ПО Профили.Профиль = ГруппыПользователей.Профиль
	|		ЛЕВОЕ СОЕДИНЕНИЕ ГруппыДоступа КАК ГруппыДоступа
	|		ПО Профили.Профиль = ГруппыДоступа.Профиль
	|		ЛЕВОЕ СОЕДИНЕНИЕ ВидыДоступа КАК ВидыДоступа
	|		ПО Профили.Профиль = ВидыДоступа.Профиль
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	Профили.Профиль КАК Профиль,
	|	ЕСТЬNULL(ПрофилиГруппДоступаРоли.Роль.Имя, """") КАК РольИмя
	|ИЗ
	|	Профили КАК Профили
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК ПрофилиГруппДоступаРоли
	|		ПО Профили.Профиль = ПрофилиГруппДоступаРоли.Ссылка
	|			И (Профили.ИдентификаторПоставляемыхДанных = &ПустойУИД
	|				ИЛИ Профили.ПоставляемыйПрофильИзменен)
	|
	|УПОРЯДОЧИТЬ ПО
	|	Профиль";
	
КонецФункции

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПриРегистрацииОбработчиковВыгрузкиДанных.
Процедура ПередВыгрузкойОбъекта(Контейнер, МенеджерВыгрузкиОбъекта, Сериализатор, Объект, Артефакты, Отказ) Экспорт
	
	// Роли расширений назначаются независимо в коробке и в сервисе.
	Если ТипЗнч(Объект) = Тип("СправочникОбъект.ПрофилиГруппДоступа") Тогда
		Справочники.ПрофилиГруппДоступа.УдалитьРолиРасширений(Объект);
	КонецЕсли;
	
	// В модели сервиса право открытия внешних отчетов и обработок не используется для пользователей областей данных,
	// поэтому проверка применяется только при переходе Коробка -> Сервис.
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(Объект) = Тип("СправочникОбъект.ПрофилиГруппДоступа") Тогда
		Профиль = Объект;
		
	ИначеЕсли ТипЗнч(Объект) = Тип("СправочникОбъект.ГруппыДоступа") И Не Объект.ЭтоГруппа Тогда
		Профиль = Объект.Профиль;
	Иначе
		Возврат;
	КонецЕсли;
	
	Если ЭтоПрофильОткрытиеВнешнихОтчетовИОбработок(Профиль) Тогда
		Отказ = Истина;
	КонецЕсли;
	
КонецПроцедуры

// См. ВыгрузкаЗагрузкаДанныхПереопределяемый.ПриРегистрацииОбработчиковВыгрузкиДанных.
Процедура ПередВыгрузкойНабораЗаписей(Контейнер, МенеджерВыгрузкиОбъекта, Сериализатор, Объект, Артефакты, Отказ) Экспорт
	
	// В модели сервиса право открытия внешних отчетов и обработок не используется для пользователей областей данных,
	// поэтому проверка применяется только при переходе Коробка -> Сервис.
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		Возврат;
	КонецЕсли;
	
	ГруппыДоступа = ГруппыДоступаПрофиляОткрытиеВнешнихОтчетовИОбработок();
	
	Индекс = Объект.Количество() - 1;
	Пока Индекс >= 0 Цикл
		Если ГруппыДоступа.Найти(Объект[Индекс].ГруппаДоступа) <> Неопределено Тогда
			Объект.Удалить(Индекс);
		КонецЕсли;
		Индекс = Индекс - 1;
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//  ИмяПараметра - Строка
//  УстановленныеПараметры - Массив из Строка
//
Процедура УстановкаПараметровСеанса(ИмяПараметра, УстановленныеПараметры) Экспорт
	
	#Область УниверсальноеОграничение
	Если ИмяПараметра = "ПараметрыОграниченияДоступа" Тогда
		ПараметрыСеанса.ПараметрыОграниченияДоступа = Новый ФиксированнаяСтруктура(Новый Структура);
		УстановленныеПараметры.Добавить("ПараметрыОграниченияДоступа");
		Возврат;
	КонецЕсли;
	
	Если ИмяПараметра = "ОтключениеОбновленияКлючейДоступа" Тогда
		ПараметрыСеанса.ОтключениеОбновленияКлючейДоступа = НовоеОтключениеОбновленияКлючейДоступа();
		УстановленныеПараметры.Добавить("ОтключениеОбновленияКлючейДоступа");
		Возврат;
	КонецЕсли;
	
	УниверсальноеОграничение = ОграничиватьДоступНаУровнеЗаписейУниверсально(Истина, Истина, Ложь);
	
	Если ИмяПараметра = "ОграничениеДоступаНаУровнеЗаписейУниверсально"
	 Или ПараметрыСеанса.ОграничениеДоступаНаУровнеЗаписейУниверсально <> УниверсальноеОграничение Тогда
		
		ПередИзменениемПараметровСеансаДляШаблонов(
			Новый Структура("ОграничениеДоступаНаУровнеЗаписейУниверсально", УниверсальноеОграничение),
			ИмяПараметра = "ОграничениеДоступаНаУровнеЗаписейУниверсально");
		
		ПараметрыСеанса.ОграничениеДоступаНаУровнеЗаписейУниверсально = УниверсальноеОграничение;
	КонецЕсли;
	
	УстановленныеПараметры.Добавить("ОграничениеДоступаНаУровнеЗаписейУниверсально");
	Если ИмяПараметра = "ОграничениеДоступаНаУровнеЗаписейУниверсально" Тогда
		Возврат;
	КонецЕсли;
	#КонецОбласти
	
	// Для корректной работы препроцессора в ограничениях доступа, требуется инициализации всех
	// параметров сеанса, которые могут быть востребованы в работе препроцессора.
	ОграничиватьДоступНаУровнеЗаписей = Константы.ОграничиватьДоступНаУровнеЗаписей.Получить();
	ИнформационнаяБазаЗаблокированаДляОбновления = ЗначениеЗаполнено(
		ОбновлениеИнформационнойБазыСлужебный.ИнформационнаяБазаЗаблокированаДляОбновления(Ложь));
	
	#Область УниверсальноеОграничение
	Если Не ОграничиватьДоступНаУровнеЗаписей
	 Или Не УниверсальноеОграничение
	 Или ИнформационнаяБазаЗаблокированаДляОбновления Тогда
		
		ПараметрыСеанса.СпискиСОтключеннымОграничениемЧтения =
			?(ИнформационнаяБазаЗаблокированаДляОбновления
				Или Не УниверсальноеОграничение, "Неопределено", "Все");
		
		ПустойНаборГруппДоступа = Справочники.НаборыГруппДоступа.ПустаяСсылка();
		
		ПараметрыСеанса.ВерсииШаблоновОграниченияДоступа   = ВерсииШаблоновОграниченияДоступа();
		ПараметрыСеанса.РазрешенныйНаборГруппДоступа       = ПустойНаборГруппДоступа;
		ПараметрыСеанса.РазрешенныйПустойНаборГруппДоступа = ПустойНаборГруппДоступа;
		ПараметрыСеанса.РазрешенныйНаборГруппПользователей = ПустойНаборГруппДоступа;
		ПараметрыСеанса.РазрешенныйПользователь            = ПустойНаборГруппДоступа;
		ПараметрыСеанса.ОбщиеПараметрыШаблоновОграниченияДоступа = "";
		ПараметрыСеанса.СпискиСОграничениемЧерезКлючиДоступаГруппДоступа  = "";
		ПараметрыСеанса.СпискиСОграничениемЧерезКлючиДоступаПользователей = "";
		ПараметрыСеанса.СпискиСОграничениемПоПолям = "";
		
		УстановленныеПараметры.Добавить("СпискиСОтключеннымОграничениемЧтения");
		УстановленныеПараметры.Добавить("ВерсииШаблоновОграниченияДоступа");
		УстановленныеПараметры.Добавить("РазрешенныйНаборГруппДоступа");
		УстановленныеПараметры.Добавить("РазрешенныйПустойНаборГруппДоступа");
		УстановленныеПараметры.Добавить("РазрешенныйНаборГруппПользователей");
		УстановленныеПараметры.Добавить("РазрешенныйПользователь");
		УстановленныеПараметры.Добавить("ОбщиеПараметрыШаблоновОграниченияДоступа");
		УстановленныеПараметры.Добавить("СпискиСОграничениемЧерезКлючиДоступаГруппДоступа");
		УстановленныеПараметры.Добавить("СпискиСОграничениемЧерезКлючиДоступаПользователей");
		УстановленныеПараметры.Добавить("СпискиСОграничениемПоПолям");
	КонецЕсли;
	#КонецОбласти
	
	Если Не ОграничиватьДоступНаУровнеЗаписей
	 Или УниверсальноеОграничение
	 Или ИнформационнаяБазаЗаблокированаДляОбновления Тогда
		
		ПараметрыСеанса.ОграничениеДоступаНаУровнеЗаписейИспользуется =
			?(ИнформационнаяБазаЗаблокированаДляОбновления
				Или УниверсальноеОграничение, "", Ложь);
		
		ПараметрыСеанса.ВсеВидыДоступаКромеСпециальных             = "";
		ПараметрыСеанса.ВидыДоступаСОтключеннымИспользованием      = "";
		ПараметрыСеанса.ВидыДоступаБезГруппДляЗначенияДоступа      = "";
		ПараметрыСеанса.ВидыДоступаСОднойГруппойДляЗначенияДоступа = "";
		
		ПараметрыСеанса.ТипыЗначенийДоступаСГруппами
			= Новый ФиксированныйМассив(Новый Массив);
		
		ПараметрыСеанса.ТаблицыСОтдельнымиНастройкамиПрав = "";
		
		ПараметрыСеанса.ИдентификаторыТаблицСОтдельнымиНастройкамиПрав
			= Новый ФиксированныйМассив(Новый Массив);
		
		ПараметрыСеанса.ТипыВладельцевНастроекПрав
			= Новый ФиксированныйМассив(Новый Массив);
		
		ПараметрыСеанса.ТаблицыРасширенийСОграничениемДоступа = "";
		
		УстановленныеПараметры.Добавить("ОграничениеДоступаНаУровнеЗаписейИспользуется");
		УстановленныеПараметры.Добавить("ВсеВидыДоступаКромеСпециальных");
		УстановленныеПараметры.Добавить("ВидыДоступаСОтключеннымИспользованием");
		УстановленныеПараметры.Добавить("ВидыДоступаБезГруппДляЗначенияДоступа");
		УстановленныеПараметры.Добавить("ВидыДоступаСОднойГруппойДляЗначенияДоступа");
		УстановленныеПараметры.Добавить("ТипыЗначенийДоступаСГруппами");
		УстановленныеПараметры.Добавить("ТаблицыСОтдельнымиНастройкамиПрав");
		УстановленныеПараметры.Добавить("ИдентификаторыТаблицСОтдельнымиНастройкамиПрав");
		УстановленныеПараметры.Добавить("ТипыВладельцевНастроекПрав");
		УстановленныеПараметры.Добавить("ТаблицыРасширенийСОграничениемДоступа");
	КонецЕсли;
	
	Если ИнформационнаяБазаЗаблокированаДляОбновления Тогда
		Возврат;
	КонецЕсли;
	
	#Область УниверсальноеОграничение
	Если УниверсальноеОграничение Тогда
		ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь, Истина);
		Возврат;
	КонецЕсли;
	#КонецОбласти
	
	Если Не ОграничиватьДоступНаУровнеЗаписей Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыСеанса.ОграничениеДоступаНаУровнеЗаписейИспользуется = Истина;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ТекущийПользователь", Пользователи.АвторизованныйПользователь());
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ЗначенияПоУмолчанию.ТипЗначенийДоступа КАК ТипЗначений
	|ИЗ
	|	РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|		ПО (ГруппыДоступаПользователи.Ссылка = ЗначенияПоУмолчанию.ГруппаДоступа)
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО (ГруппыДоступаПользователи.Пользователь = СоставыГруппПользователей.ГруппаПользователей)
	|			И (СоставыГруппПользователей.Пользователь = &ТекущийПользователь)
	|
	|СГРУППИРОВАТЬ ПО
	|	ЗначенияПоУмолчанию.ТипЗначенийДоступа
	|
	|ИМЕЮЩИЕ
	|	МИНИМУМ(ЗначенияПоУмолчанию.ВсеРазрешеныБезИсключений) = ИСТИНА";
	
	ТипыЗначенийВсеРазрешеныБезИсключений = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ТипЗначений");
	
	// Установка параметров ВсеВидыДоступаКромеСпециальных, ВидыДоступаСОтключеннымИспользованием.
	ВсеВидыДоступаКромеСпециальных        = Новый Массив;
	ВидыДоступаСОтключеннымИспользованием = Новый Массив;
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	ИспользуемыеВидыДоступа = ИспользуемыеВидыДоступа();
	
	Для Каждого СвойстваВидаДоступа Из СвойстваВидовДоступа.Массив Цикл
		ВсеВидыДоступаКромеСпециальных.Добавить(СвойстваВидаДоступа.Имя);
		
		Если ИспользуемыеВидыДоступа.Получить(СвойстваВидаДоступа.Ссылка) = Неопределено
		 Или ТипыЗначенийВсеРазрешеныБезИсключений.Найти(СвойстваВидаДоступа.Ссылка) <> Неопределено Тогда
			
			ВидыДоступаСОтключеннымИспользованием.Добавить(СвойстваВидаДоступа.Имя);
		КонецЕсли;
	КонецЦикла;
	
	ПараметрыСеанса.ВсеВидыДоступаКромеСпециальных = ВсеКомбинацииВидовДоступа(ВсеВидыДоступаКромеСпециальных);
	
	УстановленныеПараметры.Добавить("ВсеВидыДоступаКромеСпециальных");
	
	ВсеВидыДоступаКромеСпециальныхОтключены = (ВсеВидыДоступаКромеСпециальных.Количество()
		= ВидыДоступаСОтключеннымИспользованием.Количество());
	
	Если ВсеВидыДоступаКромеСпециальныхОтключены Тогда
		ПараметрыСеанса.ВидыДоступаСОтключеннымИспользованием = "Все";
	Иначе
		ПараметрыСеанса.ВидыДоступаСОтключеннымИспользованием
			= ВсеКомбинацииВидовДоступа(ВидыДоступаСОтключеннымИспользованием);
	КонецЕсли;
	
	УстановленныеПараметры.Добавить("ВидыДоступаСОтключеннымИспользованием");
	
	// Установка параметров ВидыДоступаБезГруппДляЗначенияДоступа,
	// ВидыДоступаСОднойГруппойДляЗначенияДоступа, ТипыЗначенийДоступаСГруппами.
	ПараметрыСеанса.ВидыДоступаБезГруппДляЗначенияДоступа =
		ВсеКомбинацииВидовДоступа(СвойстваВидовДоступа.БезГруппДляЗначенияДоступа);
	ПараметрыСеанса.ВидыДоступаСОднойГруппойДляЗначенияДоступа =
		ВсеКомбинацииВидовДоступа(СвойстваВидовДоступа.СОднойГруппойДляЗначенияДоступа);
	
	ТипыЗначенийДоступаСГруппами = Новый Массив;
	Для каждого КлючИЗначение Из СвойстваВидовДоступа.ТипыЗначенийДоступаСГруппами Цикл
		ТипыЗначенийДоступаСГруппами.Добавить(КлючИЗначение.Значение);
	КонецЦикла;
	ПараметрыСеанса.ТипыЗначенийДоступаСГруппами = Новый ФиксированныйМассив(ТипыЗначенийДоступаСГруппами);
	
	УстановленныеПараметры.Добавить("ВидыДоступаБезГруппДляЗначенияДоступа");
	УстановленныеПараметры.Добавить("ВидыДоступаСОднойГруппойДляЗначенияДоступа");
	УстановленныеПараметры.Добавить("ТипыЗначенийДоступаСГруппами");
	
	// Установка параметров ТаблицыСОтдельнымиНастройкамиПрав,
	// ИдентификаторыТаблицСОтдельнымиНастройкамиПрав, ТипыВладельцевНастроекПрав.
	ВозможныеПрава = ВозможныеПраваДляНастройкиПравОбъектов();
	ОтдельныеТаблицы = ВозможныеПрава.ОтдельныеТаблицы;
	ТаблицыСОтдельнымиНастройкамиПрав = "";
	ИдентификаторыТаблицСОтдельнымиНастройкамиПрав = Новый Массив;
	Для каждого КлючИЗначение Из ОтдельныеТаблицы Цикл
		ТаблицыСОтдельнымиНастройкамиПрав = ТаблицыСОтдельнымиНастройкамиПрав
			+ "|" + КлючИЗначение.Значение + ";" + Символы.ПС;
		ИдентификаторыТаблицСОтдельнымиНастройкамиПрав.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	
	ПараметрыСеанса.ТаблицыСОтдельнымиНастройкамиПрав = ТаблицыСОтдельнымиНастройкамиПрав;
	
	ПараметрыСеанса.ИдентификаторыТаблицСОтдельнымиНастройкамиПрав =
		Новый ФиксированныйМассив(ИдентификаторыТаблицСОтдельнымиНастройкамиПрав);
	
	ПараметрыСеанса.ТипыВладельцевНастроекПрав = ВозможныеПрава.ТипыВладельцев;
	
	ПолныеИмена = Справочники.ИдентификаторыОбъектовРасширений.ПолныеИменаТаблицСДанными();
	ТаблицыРасширений = СтрСоединить(ПолныеИмена, ";" + Символы.ПС + "|");
	ТаблицыРасширений = ?(ТаблицыРасширений = "", "", "|" + ТаблицыРасширений + ";" + Символы.ПС);
	
	ПараметрыСеанса.ТаблицыРасширенийСОграничениемДоступа = ТаблицыРасширений;
	
	УстановленныеПараметры.Добавить("ТаблицыСОтдельнымиНастройкамиПрав");
	УстановленныеПараметры.Добавить("ИдентификаторыТаблицСОтдельнымиНастройкамиПрав");
	УстановленныеПараметры.Добавить("ТипыВладельцевНастроекПрав");
	УстановленныеПараметры.Добавить("ТаблицыРасширенийСОграничениемДоступа");
	
КонецПроцедуры

// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//   * Стандартное - Булево
//   * Полное      - Булево
//   * ВложенныеОтключения - ФиксированныйМассив
//   * ИзмененныеСписки - ХранилищеЗначения
//
Функция НовоеОтключениеОбновленияКлючейДоступа() Экспорт
	
	Свойства = Новый Структура;
	Свойства.Вставить("Стандартное",         Ложь);
	Свойства.Вставить("Полное",              Ложь);
	Свойства.Вставить("ВложенныеОтключения", Новый ФиксированныйМассив(Новый Массив));
	Свойства.Вставить("ИзмененныеСписки",    Новый ХранилищеЗначения(Новый Соответствие));
	
	Возврат Новый ФиксированнаяСтруктура(Свойства);
	
КонецФункции

// Только для внутреннего использования.
Процедура ОбновитьПараметрыСеанса() Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	УстановленныеПараметры = Новый Массив;
	УстановкаПараметровСеанса("", УстановленныеПараметры);
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
	ОбновитьПовторноИспользуемыеЗначения();
	
КонецПроцедуры

// Проверка группы доступа Администраторы перед записью.
Процедура ПроверитьНаличиеПользователяИБВГруппеДоступаАдминистраторы(ПользователиГруппы, ОписаниеОшибки) Экспорт
	
	Пользователи.НайтиНеоднозначныхПользователейИБ(Неопределено);
	
	// Проверка пустого списка пользователей ИБ в группе доступа Администраторы.
	УстановитьПривилегированныйРежим(Истина);
	НайденДействующийАдминистратор = Ложь;
	
	Для Каждого ОписаниеПользователя Из ПользователиГруппы Цикл
		
		Если Не ЗначениеЗаполнено(ОписаниеПользователя.Пользователь)
		 Или ТипЗнч(ОписаниеПользователя.Пользователь) <> Тип("СправочникСсылка.Пользователи")
		   И ТипЗнч(ОписаниеПользователя.Пользователь) <> Тип("СправочникСсылка.Пользователи") Тогда
			Продолжить;
		КонецЕсли;
		
		ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
			ОписаниеПользователя.Пользователь.ИдентификаторПользователяИБ);
		
		Если ПользовательИБ <> Неопределено
		   И Пользователи.ВходВПрограммуРазрешен(ПользовательИБ) Тогда
			
			НайденДействующийАдминистратор = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если НЕ НайденДействующийАдминистратор Тогда
		ОписаниеОшибки =
			НСтр("ru = 'В группе доступа Администраторы
			           |должен быть хотя бы один пользователь,
			           |которому разрешен вход в программу.'");
	КонецЕсли;
	
КонецПроцедуры

// См. УправлениеДоступом.ЕстьОграничениеТаблицыПоВидуДоступа
Функция ЕстьОграничениеТаблицыПоВидуДоступа(Таблица, ВидДоступа, ВсеВидыДоступа) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	ВидыДоступаСОтключеннымИспользованием = ПараметрыСеанса.ВидыДоступаСОтключеннымИспользованием;
	Если ВидыДоступаСОтключеннымИспользованием = "Все"
	 Или СтрНайти(ВидыДоступаСОтключеннымИспользованием, "," + ВидДоступа + ",") > 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	МассивВидовДоступа = СтрРазделить(ВсеВидыДоступа, ",", Ложь);
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка в функции %1 общего модуля %2.'"),
		"ЕстьОграничениеТаблицыПоВидуДоступа", "УправлениеДоступом")
		+ Символы.ПС;
	
	СвойстваВидаДоступа = СвойстваВидовДоступа.ПоИменам.Получить(ВидДоступа);
	Если СвойстваВидаДоступа = Неопределено Тогда
		ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не существует вид доступа ""%1"", указанный в параметре %2.'"),
			ВидДоступа, "ВидДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	ВидДоступаСсылка = СвойстваВидаДоступа.Ссылка;
	
	ВсеВидыДоступаТаблицыСОтключеннымИспользованием = Истина;
	ИспользованиеВидовДоступа = Новый Соответствие;
	ВидДоступаУказанВоВсехВидахДоступа = Ложь;
	
	Для Каждого ТекущийВидДоступа Из МассивВидовДоступа Цикл
		ТекущийВидДоступа = СокрЛП(ТекущийВидДоступа);
		СвойстваВидаДоступа = СвойстваВидовДоступа.ПоИменам.Получить(ТекущийВидДоступа);
		Если СвойстваВидаДоступа = Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не существует вид доступа ""%1"", указанный в параметре
				           |%2: ""%3"".'"),
				ТекущийВидДоступа, "ВсеВидыДоступа", ВсеВидыДоступа);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		Если СвойстваВидаДоступа.Имя = ВидДоступа Тогда
			ВидДоступаУказанВоВсехВидахДоступа = Истина;
		КонецЕсли;
		Используется = СтрНайти(ВидыДоступаСОтключеннымИспользованием, "," + СвойстваВидаДоступа.Имя + ",") = 0;
		ИспользованиеВидовДоступа.Вставить(СвойстваВидаДоступа.Ссылка, Используется);
		Если Используется Тогда
			ВсеВидыДоступаТаблицыСОтключеннымИспользованием = Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ВидДоступаУказанВоВсехВидахДоступа Тогда
		ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Вид доступа ""%1"", указанный в параметре %2 не существует в параметре
			           |%3: ""%4"".'"),
			ВидДоступа, "ВидДоступа", "ВсеВидыДоступа", ВсеВидыДоступа);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ВсеВидыДоступаТаблицыСОтключеннымИспользованием Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИмяОсновнойТаблицыСписка", Таблица);
	Запрос.УстановитьПараметр("АвторизованныйПользователь", Пользователи.АвторизованныйПользователь());
	
	Запрос.Текст = ТекстЗапросаГруппДоступа();
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ЗначенияПоУмолчанию.ГруппаДоступа КАК ГруппаДоступа,
	|	ЗначенияПоУмолчанию.ТипЗначенийДоступа КАК ВидДоступа
	|ИЗ
	|	РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияПоУмолчанию
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ГруппыДоступаПользователя КАК ГруппыДоступаПользователя
	|		ПО ЗначенияПоУмолчанию.ГруппаДоступа = ГруппыДоступаПользователя.Ссылка
	|ГДЕ
	|	НЕ ЗначенияПоУмолчанию.ВсеРазрешеныБезИсключений
	|	И ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|					ПО
	|						ГруппыДоступаПользователи.Ссылка = ЗначенияПоУмолчанию.ГруппаДоступа
	|							И ГруппыДоступаПользователи.Пользователь = СоставыГруппПользователей.ГруппаПользователей
	|							И СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь)";
	
	ДобавитьЗапросВПакет(Запрос.Текст, ТекстЗапроса);
	Выборка = Запрос.Выполнить().Выбрать();
	
	НастроенныеВидыДоступаГруппДоступа = Новый Соответствие;
	ЕстьГруппаДоступаСОграничениемПоВидуДоступа = Ложь;
	
	Пока Выборка.Следующий() Цикл
		НастроенныеВидыДоступа = НастроенныеВидыДоступаГруппДоступа.Получить(Выборка.ГруппаДоступа);
		Если НастроенныеВидыДоступа = Неопределено Тогда
			НастроенныеВидыДоступа = Новый Соответствие;
			НастроенныеВидыДоступаГруппДоступа.Вставить(Выборка.ГруппаДоступа, НастроенныеВидыДоступа);
		КонецЕсли;
		Если ИспользованиеВидовДоступа.Получить(Выборка.ВидДоступа) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		НастроенныеВидыДоступа.Вставить(Выборка.ВидДоступа, Истина);
		Если Выборка.ВидДоступа = ВидДоступаСсылка Тогда
			ЕстьГруппаДоступаСОграничениемПоВидуДоступа = Истина;
		КонецЕсли;
	КонецЦикла;
	
	ЕстьГруппаДоступаБезОграниченийПоВсемВидамДоступа = Ложь;
	
	Для Каждого ОписаниеГруппыДоступа Из НастроенныеВидыДоступаГруппДоступа Цикл
		НастроенныеВидыДоступа = ОписаниеГруппыДоступа.Значение;
		ВсеВидыДоступаТаблицыБезОграниченийВГруппеДоступа = Истина;
		Для Каждого ОписаниеИспользованияВидаДоступа Из ИспользованиеВидовДоступа Цикл
			Если Не ОписаниеИспользованияВидаДоступа.Значение Тогда
				Продолжить; // Не используется.
			КонецЕсли;
			Если НастроенныеВидыДоступа.Получить(ОписаниеИспользованияВидаДоступа.Ключ) = Неопределено Тогда
				Продолжить; // ВсеРазрешеныБезИсключений или ограничения по виду доступа нет.
			КонецЕсли;
			ВсеВидыДоступаТаблицыБезОграниченийВГруппеДоступа = Ложь;
			Прервать;
		КонецЦикла;
		Если ВсеВидыДоступаТаблицыБезОграниченийВГруппеДоступа Тогда
			ЕстьГруппаДоступаБезОграниченийПоВсемВидамДоступа = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если ЕстьГруппаДоступаБезОграниченийПоВсемВидамДоступа Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат ЕстьГруппаДоступаСОграничениемПоВидуДоступа;
	
КонецФункции

// Параметры:
//  Объекты            - см. ОбновлениеИнформационнойБазы.ДобавитьОбъектПланируемыйКУдалению.Объекты
//  ПолноеИмяИзмерения - см. ОбновлениеИнформационнойБазы.ДобавитьОбъектПланируемыйКУдалению.Объект
//  ТребуемыеТипы      - ОписаниеТипов
//  УказанныеТипы      - ОписаниеТипов
//
Процедура ДобавитьОбъектПланируемыйКУдалению(Объекты, ТребуемыеТипы, МетаданныеИзмерения)
	
	Если ТребуемыеТипы.Типы().Количество() = 0 Тогда
		ЛишниеТипы = МетаданныеИзмерения.Тип;
	Иначе
		ЛишниеТипы = Новый ОписаниеТипов(МетаданныеИзмерения.Тип,, ТребуемыеТипы.Типы());
	КонецЕсли;
	
	Если ЛишниеТипы.Типы().Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ЧастиИмени = СтрРазделить(МетаданныеИзмерения.ПолноеИмя(), ".");
	ЧастиИмени.Удалить(2);
	
	ОбновлениеИнформационнойБазы.ДобавитьОбъектПланируемыйКУдалению(Объекты,
		СтрСоединить(ЧастиИмени, "."), ЛишниеТипы);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Обработчики подписок на события.

// Обработчик подписки ОбновитьГруппыЗначенийДоступа на событие ПередЗаписью:
// - вызывает метод записи групп значений доступа в
//   регистр сведений ГруппыЗначенийДоступа для требуемых объектов метаданных.
//
Процедура ОбновитьГруппыЗначенийДоступа(Знач Объект, Отказ) Экспорт
	
	Если Объект.ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоИдентификаторОбъектаМетаданных(Объект) Тогда
		Возврат;
	КонецЕсли;
	
	Если Объект.ЭтоНовый()
	   И ОбщегоНазначения.ИнформационнаяБазаФайловая()
	   И Не ПропуститьПроверкуДоступа(Отказ, Объект) Тогда
		
		Блокировка = Новый БлокировкаДанных;
		Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
		Блокировка.Заблокировать();
	КонецЕсли;
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	ЗначенияДоступаСГруппами = СвойстваВидовДоступа.ЗначенияДоступаСГруппами;
	
	Если ЗначенияДоступаСГруппами.ПоТипамДляОбновления.Получить(ТипЗнч(Объект)) <> Неопределено Тогда
		РегистрыСведений.ГруппыЗначенийДоступа.ОбновитьГруппыЗначенийДоступа(Объект);
	КонецЕсли;
	
	Если СвойстваВидовДоступа.ПоТипамЗначенийСИерархией.Получить(ТипЗнч(Объект)) <> Неопределено Тогда
		Родитель = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Объект.Ссылка, "Родитель");
		Если Объект.Родитель <> Родитель Тогда
			Объект.ДополнительныеСвойства.Вставить("ОбновитьЗначенияГруппДоступа");
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Обработчик подписки ОбновитьЗначенияГруппДоступа на событие ПриЗаписи:
// - обновляет значения групп доступа, выбранные с учетом иерархии.
//
Процедура ОбновитьЗначенияГруппДоступа(Источник) Экспорт
	
	Если Источник.ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	
	Если Источник.ДополнительныеСвойства.Свойство("ОбновитьЗначенияГруппДоступа") Тогда
		ГруппыДоступа = ГруппыДоступаИспользующиеИерархиюЗначенийДоступа(ТипЗнч(Источник.Ссылка));
		РегистрыСведений.ЗначенияГруппДоступа.ОбновитьДанныеРегистра(ГруппыДоступа);
	КонецЕсли;
	
КонецПроцедуры

// Обработчик подписки ОбновитьГруппыВладельцевНастроекПрав на событие ПередЗаписью:
// - вызывает метод записи иерархии владельцев настроек прав объектов в
//   регистр сведений НаследованиеНастроекПравОбъектов для требуемых объектов метаданных.
//
Процедура ОбновитьГруппыВладельцевНастроекПрав(Знач Объект, Отказ) Экспорт
	
	Если Объект.ОбменДанными.Загрузка Тогда
		Возврат;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоИдентификаторОбъектаМетаданных(Объект) Тогда
		Возврат;
	КонецЕсли;
	
	ВозможныеПрава = ВозможныеПраваДляНастройкиПравОбъектов();
	ВозможныеПраваПоТипам = ВозможныеПрава.ПоТипам;
	
	Если ВозможныеПраваПоТипам.Получить(ТипЗнч(Объект)) <> Неопределено Тогда
		РегистрыСведений.НаследованиеНастроекПравОбъектов.ОбновитьДанныеРегистра(Объект);
	КонецЕсли;
	
КонецПроцедуры

// Обработчик подписки ЗаписатьНаборыЗначенийДоступа на событие ПриЗаписи
// вызывает метод записи значений доступа объекта в РегистрСведений.НаборыЗначенийДоступа.
//  Возможен случай использования подсистемы "УправлениеДоступом", когда
// указанной подписки не существует, если наборы значений доступа не применяются.
//
Процедура ЗаписатьНаборыЗначенийДоступаПриЗаписи(Знач Объект, Отказ) Экспорт
	
	// Проверка ОбменДанными.Загрузка пропускается только в тех случаях,
	// когда дополнительно установлено свойство ЗаписатьНаборыЗначенийДоступа.
	// В этом случае при записи ведущего объекта для корректной работы его RLS выполняется программная
	// запись подчиненного объекта для обновления служебной табличной части НаборыЗначенийДоступа.
	Если Объект.ОбменДанными.Загрузка
	   И НЕ Объект.ДополнительныеСвойства.Свойство("ЗаписатьНаборыЗначенийДоступа") Тогда
		
		Возврат;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоИдентификаторОбъектаМетаданных(Объект) Тогда
		Возврат;
	КонецЕсли;
	
	ЗаписатьНаборыЗначенийДоступа(Объект, , Объект.ДополнительныеСвойства.Свойство(
		"ЗаписьНаборовЗначенийДоступаПриОбновленииИБ"));
	
КонецПроцедуры

// Обработчик подписки ЗаписатьЗависимыеНаборыЗначенийДоступа события ПриЗаписи
// вызывает перезапись зависимых наборов значений доступа в регистре сведений НаборыЗначенийДоступа.
//
//  Возможен случай использования подсистемы "УправлениеДоступом", когда
// указанной подписки не существует, если зависимые наборы значений доступа не применяются.
//
Процедура ЗаписатьЗависимыеНаборыЗначенийДоступаПриЗаписи(Знач Объект, Отказ) Экспорт
	
	// Проверка ОбменДанными.Загрузка пропускается только в тех случаях,
	// когда дополнительно установлено свойство ЗаписатьНаборыЗначенийДоступа.
	// В этом случае при записи ведущего объекта для корректной работы его RLS выполняется программная
	// запись подчиненного объекта для обновления служебной табличной части НаборыЗначенийДоступа.
	Если Объект.ОбменДанными.Загрузка
	   И НЕ Объект.ДополнительныеСвойства.Свойство("ЗаписатьЗависимыеНаборыЗначенийДоступа") Тогда
		
		Возврат;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоИдентификаторОбъектаМетаданных(Объект) Тогда
		Возврат;
	КонецЕсли;
	
	ЗаписатьЗависимыеНаборыЗначенийДоступа(Объект, Объект.ДополнительныеСвойства.Свойство(
		"ЗаписьНаборовЗначенийДоступаПриОбновленииИБ"));
	
КонецПроцедуры

// Обработчик подписок ЗаполнитьНаборыЗначенийДоступаТабличныхЧастей* на событие ПередЗаписью
// вызывает заполнение значений доступа табличной части объекта НаборыЗначенийДоступа,
// когда для ограничения доступа к самому объекту используется шаблон #ПоНаборамЗначений.
//  Возможен случай использования подсистемы Управление доступом, когда
// указанной подписки не существует, если для указанной цели наборы не применяются.
//
// Параметры:
//  Источник        - СправочникОбъект
//                  - ДокументОбъект
//                  - ПланВидовХарактеристикОбъект
//                  - ПланСчетовОбъект
//                  - ПланВидовРасчетаОбъект
//                  - БизнесПроцессОбъект
//                  - ЗадачаОбъект
//                  - ПланОбменаОбъект - объект данных, передаваемый в подписку на событие ПередЗаписью.
//
//  Отказ           - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//
//  РежимЗаписи     - Булево - параметр, передаваемый в подписку на событие ПередЗаписью,
//                    когда тип параметра Источник - ДокументОбъект.
//
//  РежимПроведения - Булево - параметр, передаваемый в подписку на событие ПередЗаписью,
//                    когда тип параметра Источник - ДокументОбъект.
//
Процедура ЗаполнитьНаборыЗначенийДоступаТабличныхЧастей(Источник, Отказ = Неопределено, РежимЗаписи = Неопределено, РежимПроведения = Неопределено) Экспорт
	
	// Проверка ОбменДанными.Загрузка пропускается только в тех случаях,
	// когда дополнительно установлено свойство ЗаписатьНаборыЗначенийДоступа.
	// В этом случае при записи ведущего объекта для корректной работы его RLS выполняется программная
	// запись подчиненного объекта для обновления служебной табличной части НаборыЗначенийДоступа.
	Если Источник.ОбменДанными.Загрузка
	   И НЕ Источник.ДополнительныеСвойства.Свойство("ЗаписатьНаборыЗначенийДоступа") Тогда
		Возврат;
	КонецЕсли;
	
	Если СтандартныеПодсистемыСервер.ЭтоИдентификаторОбъектаМетаданных(Источник) Тогда
		Возврат;
	КонецЕсли;
	
	Если НЕ (  ПривилегированныйРежим()
	         И Источник.ДополнительныеСвойства.Свойство(
	             "НаборыЗначенийДоступаТабличнойЧастиЗаполнены")) Тогда
		
		Если Источник.ЭтоНовый()
		   И ОбщегоНазначения.ИнформационнаяБазаФайловая()
		   И Не ПропуститьПроверкуДоступа(Отказ, Источник) Тогда
			
			ПредварительнаяБлокировкаПередЗаписьюНовогоВФайловойИБ();
		КонецЕсли;
		
		Таблица = ПолучитьНаборыЗначенийДоступаТабличнойЧасти(Источник);
		ПодготовитьНаборыЗначенийДоступаКЗаписи(Неопределено, Таблица, Ложь);
		Источник.НаборыЗначенийДоступа.Загрузить(Таблица);
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Обработчики регламентных заданий.

// Обработчик регламентного задания ЗаполнениеДанныхДляОграниченияДоступа.
Процедура ЗаполнениеДанныхДляОграниченияДоступаОбработчикЗадания() Экспорт
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(
		Метаданные.РегламентныеЗадания.ЗаполнениеДанныхДляОграниченияДоступа);
	
	ЗаполнениеДанныхДляОграниченияДоступа();
	
КонецПроцедуры

// Выполняет последовательное заполнение и обновление данных, необходимых для работы
// подсистемы УправленияДоступом в режиме ограничения доступа на уровне записей.
// 
//  При включенном режиме ограничения доступа на уровне записей заполняет наборы
// значений доступа. Заполнение выполняется частями при каждом запуске, пока все
// наборы значений доступа не будут заполнены.
//  При отключении режима ограничения доступа на уровне записей наборы значений доступа
// (заполненные ранее) удаляются при перезаписи объектов, а не все сразу.
//  Независимо от режима ограничения доступа на уровне записей обновляет кэш-реквизиты.
//  После завершения всех обновлений и заполнений отключает использование регламентного задания.
//
//  Сведения о состоянии работы записываются в журнал регистрации.
//
//  Возможно вызывать программно, например, при обновлении информационной базы.
// Также для целей обновления есть форма Справочник.ГруппыДоступа.ОбновлениеДанныхОграниченияДоступа,
// с помощью которой можно сделать интерактивное обновление данных ограничения доступа
// при обновлении информационной базы.
//
Процедура ЗаполнениеДанныхДляОграниченияДоступа(КоличествоДанных = 0, ТолькоКэшРеквизиты = Ложь, ЕстьИзменения = Неопределено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	ЗначенияДоступаСГруппами = СвойстваВидовДоступа.ЗначенияДоступаСГруппами;
	
	Если Константы.ОграничиватьДоступНаУровнеЗаписей.Получить() И НЕ ТолькоКэшРеквизиты Тогда
		
		// Заполнение групп значений доступа в регистре сведений ГруппыЗначенийДоступа.
		Для Каждого ИмяТаблицы Из ЗначенияДоступаСГруппами.ИменаТаблицДляОбновления Цикл
			
			Если КоличествоДанных < 10000 Тогда
				
				Запрос = Новый Запрос;
				Запрос.Текст =
				"ВЫБРАТЬ
				|	ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка) КАК Ссылка
				|ГДЕ
				|	НЕ ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА КАК ЗначениеИстина
				|				ИЗ
				|					РегистрСведений.ГруппыЗначенийДоступа КАК ГруппыЗначенийДоступа
				|				ГДЕ
				|					ГруппыЗначенийДоступа.ЗначениеДоступа = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
				|					И ГруппыЗначенийДоступа.ГруппаДанных = 0)
				|
				|ОБЪЕДИНИТЬ ВСЕ
				|
				|ВЫБРАТЬ ПЕРВЫЕ 10000
				|	ТекущаяТаблица.Ссылка
				|ИЗ
				|	&ТекущаяТаблица КАК ТекущаяТаблица
				|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ГруппыЗначенийДоступа КАК ГруппыЗначенийДоступа
				|		ПО ТекущаяТаблица.Ссылка = ГруппыЗначенийДоступа.ЗначениеДоступа
				|			И (ГруппыЗначенийДоступа.ГруппаДанных = 0)
				|ГДЕ
				|	ГруппыЗначенийДоступа.ЗначениеДоступа ЕСТЬ NULL";
				
				Запрос.Текст = СтрЗаменить(Запрос.Текст, "Справочник.Пользователи", ИмяТаблицы);
				Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ТекущаяТаблица", ИмяТаблицы);
				// @skip-check query-in-loop - Порционная обработка данных
				Значения = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
				
				РегистрыСведений.ГруппыЗначенийДоступа.ОбновитьГруппыЗначенийДоступа(Значения, ЕстьИзменения);
				
				КоличествоДанных = КоличествоДанных + Значения.Количество();
			КонецЕсли;
			
		КонецЦикла;
		
		Если КоличествоДанных < 10000 И Не УправлениеДоступом.ПроизводительныйВариант() Тогда
			
			// Заполнение регистра сведений НаборыЗначенийДоступа.
			ТипыОбъектов = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
				"ЗаписатьНаборыЗначенийДоступа");
			
			Для каждого ОписаниеТипа Из ТипыОбъектов Цикл
				Тип = ОписаниеТипа.Ключ;
				
				Если КоличествоДанных < 10000 И Тип <> Тип("Строка") Тогда
				
					Запрос = Новый Запрос;
					Запрос.Текст =
					"ВЫБРАТЬ ПЕРВЫЕ 10000
					|	ТекущаяТаблица.Ссылка КАК Ссылка
					|ИЗ
					|	&ТекущаяТаблица КАК ТекущаяТаблица
					|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.НаборыЗначенийДоступа КАК РегистрСведенийНаборыЗначенийДоступа
					|		ПО ТекущаяТаблица.Ссылка = РегистрСведенийНаборыЗначенийДоступа.Объект
					|ГДЕ
					|	РегистрСведенийНаборыЗначенийДоступа.Объект ЕСТЬ NULL ";
					Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ТекущаяТаблица", Метаданные.НайтиПоТипу(Тип).ПолноеИмя());
					// @skip-check query-in-loop - Порционная обработка данных
					Выборка = Запрос.Выполнить().Выбрать();
					КоличествоДанных = КоличествоДанных + Выборка.Количество();
					
					Пока Выборка.Следующий() Цикл
						// @skip-check query-in-loop - Порционная обработка данных
						ОбновитьНаборыЗначенийДоступа(Выборка.Ссылка, ЕстьИзменения);
					КонецЦикла;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
	// Обновление кэш-реквизитов в наборах значений доступа.
	Если КоличествоДанных < 10000 И Не УправлениеДоступом.ПроизводительныйВариант() Тогда
		
		ТипыЗначенийДоступа          = СвойстваВидовДоступа.ПоТипамЗначений;
		ТипыЗначенийДоступаСГруппами = СвойстваВидовДоступа.ТипыЗначенийДоступаСГруппами;
		
		ТаблицаТиповЗначений = Новый ТаблицаЗначений;
		ТаблицаТиповЗначений.Колонки.Добавить("ТипЗначений", Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
		Для каждого КлючИЗначение Из ТипыЗначенийДоступа Цикл
			ТаблицаТиповЗначений.Добавить().ТипЗначений = ПустаяСсылкаОбъектаМетаданных(КлючИЗначение.Ключ);
		КонецЦикла;
		
		ТаблицаТиповЗначенийСГруппами = Новый ТаблицаЗначений;
		ТаблицаТиповЗначенийСГруппами.Колонки.Добавить("ТипЗначений", Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
		Для каждого КлючИЗначение Из ТипыЗначенийДоступаСГруппами Цикл
			ТаблицаТиповЗначенийСГруппами.Добавить().ТипЗначений = ПустаяСсылкаОбъектаМетаданных(КлючИЗначение.Ключ);
		КонецЦикла;
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ТаблицаТиповЗначений", ТаблицаТиповЗначений);
		Запрос.УстановитьПараметр("ТаблицаТиповЗначенийСГруппами", ТаблицаТиповЗначенийСГруппами);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ТаблицаТипов.ТипЗначений
		|ПОМЕСТИТЬ ТаблицаТиповЗначений
		|ИЗ
		|	&ТаблицаТиповЗначений КАК ТаблицаТипов
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	ТаблицаТипов.ТипЗначений
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ТаблицаТипов.ТипЗначений
		|ПОМЕСТИТЬ ТаблицаТиповЗначенийСГруппами
		|ИЗ
		|	&ТаблицаТиповЗначенийСГруппами КАК ТаблицаТипов
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	ТаблицаТипов.ТипЗначений
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ ПЕРВЫЕ 10000
		|	НаборыЗначенийДоступа.Объект,
		|	НаборыЗначенийДоступа.НомерНабора,
		|	НаборыЗначенийДоступа.ЗначениеДоступа,
		|	НаборыЗначенийДоступа.Уточнение,
		|	НаборыЗначенийДоступа.Чтение,
		|	НаборыЗначенийДоступа.Изменение
		|ИЗ
		|	РегистрСведений.НаборыЗначенийДоступа КАК НаборыЗначенийДоступа
		|ГДЕ
		|	ВЫБОР
		|			КОГДА НаборыЗначенийДоступа.СтандартноеЗначение <> ИСТИНА В
		|					(ВЫБРАТЬ ПЕРВЫЕ 1
		|						ИСТИНА
		|					ИЗ
		|						ТаблицаТиповЗначений КАК ТаблицаТиповЗначений
		|					ГДЕ
		|						ТИПЗНАЧЕНИЯ(ТаблицаТиповЗначений.ТипЗначений) = ТИПЗНАЧЕНИЯ(НаборыЗначенийДоступа.ЗначениеДоступа))
		|				ТОГДА ИСТИНА
		|			КОГДА НаборыЗначенийДоступа.СтандартноеЗначение = ИСТИНА
		|				ТОГДА НаборыЗначенийДоступа.ЗначениеБезГрупп = ИСТИНА В
		|						(ВЫБРАТЬ ПЕРВЫЕ 1
		|							ИСТИНА
		|						ИЗ
		|							ТаблицаТиповЗначенийСГруппами КАК ТаблицаТиповЗначенийСГруппами
		|						ГДЕ
		|							ТИПЗНАЧЕНИЯ(ТаблицаТиповЗначенийСГруппами.ТипЗначений) = ТИПЗНАЧЕНИЯ(НаборыЗначенийДоступа.ЗначениеДоступа))
		|			ИНАЧЕ НаборыЗначенийДоступа.ЗначениеБезГрупп = ИСТИНА
		|		КОНЕЦ";
		Выборка = Запрос.Выполнить().Выбрать();
		КоличествоДанных = КоличествоДанных + Выборка.Количество();
		
		Пока Выборка.Следующий() Цикл
			МенеджерЗаписи = РегистрыСведений.НаборыЗначенийДоступа.СоздатьМенеджерЗаписи();
			ЗаполнитьЗначенияСвойств(МенеджерЗаписи, Выборка);
			
			ТипЗначенияДоступа = ТипЗнч(Выборка.ЗначениеДоступа);
			
			Если ТипыЗначенийДоступа.Получить(ТипЗначенияДоступа) <> Неопределено Тогда
				МенеджерЗаписи.СтандартноеЗначение = Истина;
				Если ТипыЗначенийДоступаСГруппами.Получить(ТипЗначенияДоступа) = Неопределено Тогда
					МенеджерЗаписи.ЗначениеБезГрупп = Истина;
				КонецЕсли;
			КонецЕсли;
			
			МенеджерЗаписи.Записать();
			ЕстьИзменения = Истина;
		КонецЦикла;
	КонецЕсли;
	
	Если КоличествоДанных < 10000 Тогда
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Управление доступом.Заполнение данных для ограничения доступа'",
				 ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Информация,
			,
			,
			НСтр("ru = 'Завершено заполнение данных для ограничения доступа.'"),
			РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
			
		УстановитьЗаполнениеДанныхДляОграниченияДоступа(Ложь);
	Иначе
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Управление доступом.Заполнение данных для ограничения доступа'",
				 ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Информация,
			,
			,
			НСтр("ru = 'Выполнена запись части данных для ограничения доступа.'"),
			РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
	КонецЕсли;
	
КонецПроцедуры

// Устанавливает использование регламентного задания заполнения данных управления доступом.
//
// Параметры:
//   Использование - Булево - Истина, если задание нужно включить, иначе Ложь.
//
Процедура УстановитьЗаполнениеДанныхДляОграниченияДоступа(Знач Использование) Экспорт
	
	РегламентныеЗаданияСервер.УстановитьИспользованиеПредопределенногоРегламентногоЗадания(
		Метаданные.РегламентныеЗадания.ЗаполнениеДанныхДляОграниченияДоступа, Использование);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для работы с видами доступа.

// Возвращает Истина, если вид доступа включен по функциональным опциям для всех сеансов.
//
// Параметры:
//  ВидДоступа - ОпределяемыйТип.ЗначениеДоступа - пустая ссылка основного типа вида доступа.
//             - Строка - имя вида доступа.
//
// Возвращаемое значение:
//  Булево
//
Функция ВидДоступаИспользуется(Знач ВидДоступа) Экспорт
	
	Если Не УправлениеДоступом.ОграничиватьДоступНаУровнеЗаписей() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	СвойстваВидаДоступа = СвойстваВидаДоступа(ВидДоступа);
	Если СвойстваВидаДоступа = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ТипЗначенийДоступа", СвойстваВидаДоступа.Ссылка);
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.ИспользуемыеВидыДоступа КАК ИспользуемыеВидыДоступа
	|ГДЕ
	|	ИспользуемыеВидыДоступа.ТипЗначенийДоступа = &ТипЗначенийДоступа";
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Используется = Не Запрос.Выполнить().Пустой();
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
	Возврат Используется;
	
КонецФункции

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - ОпределяемыйТип.ЗначениеДоступа - пустая ссылка основного типа вида доступа.
//   * Значение - Булево - значение Истина.
//
Функция ИспользуемыеВидыДоступа() Экспорт
	
	Результат = Новый Соответствие;
	
	Если Не УправлениеДоступом.ОграничиватьДоступНаУровнеЗаписей() Тогда
		Возврат Результат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ИспользуемыеВидыДоступа.ТипЗначенийДоступа КАК ТипЗначенийДоступа
	|ИЗ
	|	РегистрСведений.ИспользуемыеВидыДоступа КАК ИспользуемыеВидыДоступа";
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	
	Пока Выборка.Следующий() Цикл
		Если СвойстваВидовДоступа.ПоСсылкам.Получить(Выборка.ТипЗначенийДоступа) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Результат.Вставить(Выборка.ТипЗначенийДоступа, Истина);
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Возвращает свойства вида доступа или всех видов доступа.
//
// Параметры:
//  ВидДоступа - ОпределяемыйТип.ЗначениеДоступа - пустая ссылка основного типа вида доступа.
//             - Строка - имя вида доступа.
//
// Возвращаемое значение:
//   Структура:
//     * Имя - Строка
//     * Ссылка - ОпределяемыйТип.ЗначениеДоступа
//     * ТипЗначений - Тип
//     * ТипГруппЗначений - Тип
//     * НесколькоГруппЗначений - Булево
//     * ДополнительныеТипы - ФиксированныйМассив из см. ДополнительныйТипВидаДоступа
//     * ТипыВыбираемыхЗначений - ФиксированныйМассив из Тип
//   Неопределено
//
Функция СвойстваВидаДоступа(ВидДоступа) Экспорт
	
	Свойства = СвойстваВидовДоступа();
	
	Если ТипЗнч(ВидДоступа) = Тип("Строка") Тогда
		СвойстваВидаДоступа = Свойства.ПоИменам.Получить(ВидДоступа);
	Иначе
		СвойстваВидаДоступа = Свойства.ПоСсылкам.Получить(ВидДоступа);
	КонецЕсли;
	
	Возврат СвойстваВидаДоступа;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для работы с наборами значений доступа.

// Возвращает новые наборы для заполнения табличной части.
//
// Параметры:
//  Объект - ОпределяемыйТип.ВладелецСОграничениемПоНаборамЗначенийДоступаОбъект
//         - ОпределяемыйТип.ВладелецСОграничениемПоНаборамЗначенийДоступаДокумент
//
// Возвращаемое значение:
//   см. УправлениеДоступом.ТаблицаНаборыЗначенийДоступа
//
Функция ПолучитьНаборыЗначенийДоступаТабличнойЧасти(Объект)
	
	ТипЗначенияОбъект = ТипЗнч(Объект);
	
	Если Объект.Метаданные().ТабличныеЧасти.Найти("НаборыЗначенийДоступа") = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неверные параметры.
			           |У объекта типа ""%1""
			           |не существует табличная часть %2.'"),
			ТипЗначенияОбъект, "НаборыЗначенийДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Таблица = УправлениеДоступом.ТаблицаНаборыЗначенийДоступа();
	
	Если НЕ УправлениеДоступом.ОграничиватьДоступНаУровнеЗаписей() Тогда
		Возврат Таблица;
	КонецЕсли;
	
	УправлениеДоступом.ЗаполнитьНаборыЗначенийДоступа(Объект, Таблица);
	
	УправлениеДоступом.ДобавитьНаборыЗначенийДоступа(
		Таблица, УправлениеДоступом.ТаблицаНаборыЗначенийДоступа(), Ложь, Истина);
	
	Возврат Таблица;
	
КонецФункции

// Выполняет обновление наборов значений доступа объекта, если они изменились.
// Наборы обновляются в табличной части (если используется) и
// в регистре сведений НаборыЗначенийДоступа.
//
// Параметры:
//  СсылкаИлиОбъект - ЛюбаяСсылка
//                  - ОпределяемыйТип.ВладелецНаборовЗначенийДоступаОбъект - ссылка или объект,
//                    для которого записываются наборы значений доступа.
//
//  ЕстьИзменения - Булево
//                - Неопределено
//  
//  ОбновлениеИБ    - Булево - если Истина, то необходимо выполнять запись данных,
//                            не выполняя лишних, избыточных действий с данными.
//                            См. ОбновлениеИнформационнойБазы.ЗаписатьДанные.
//
Процедура ОбновитьНаборыЗначенийДоступа(СсылкаИлиОбъект, ЕстьИзменения = Неопределено, ОбновлениеИБ = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	Объект = ?(СсылкаИлиОбъект = СсылкаИлиОбъект.Ссылка, СсылкаИлиОбъект.ПолучитьОбъект(), СсылкаИлиОбъект);
	СсылкаНаОбъект = Объект.Ссылка;
	ТипЗначенияОбъект = ТипЗнч(Объект);
	
	НаборыЗаписываются = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
		"ЗаписатьНаборыЗначенийДоступа").Получить(ТипЗначенияОбъект) <> Неопределено;
	
	Если НЕ НаборыЗаписываются Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неверные параметры.
			           |Тип объекта ""%1""
			           |не существует в подписке на событие %2.'"),
			ТипЗначенияОбъект,
			"ЗаписатьНаборыЗначенийДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Метаданные.РегистрыСведений.НаборыЗначенийДоступа.Измерения.Объект.Тип.Типы().Найти(ТипЗнч(СсылкаНаОбъект)) = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи наборов значений доступа:
			           |в регистре сведений %1 в измерении %2
			           |не задан тип ""%3""'"),
			"НаборыЗначенийДоступа",
			"Объект",
			ТипЗнч(СсылкаНаОбъект));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если СсылкаНаОбъект.Метаданные().ТабличныеЧасти.Найти("НаборыЗначенийДоступа") <> Неопределено Тогда
		// Обновление объекта требуется.
		Таблица = ПолучитьНаборыЗначенийДоступаТабличнойЧасти(Объект);
		
		Если НаборыЗначенийДоступаТабличнойЧастиИзменены(СсылкаНаОбъект, Таблица) Тогда
			ПодготовитьНаборыЗначенийДоступаКЗаписи(Неопределено, Таблица, Ложь);
			
			Объект.ДополнительныеСвойства.Вставить("ЗаписатьНаборыЗначенийДоступа");
			Объект.ДополнительныеСвойства.Вставить("ЗаписатьЗависимыеНаборыЗначенийДоступа");
			Объект.ДополнительныеСвойства.Вставить("НаборыЗначенийДоступаТабличнойЧастиЗаполнены");
			Объект.НаборыЗначенийДоступа.Загрузить(Таблица);
			Если ОбновлениеИБ Тогда
				Объект.ДополнительныеСвойства.Вставить("ЗаписьНаборовЗначенийДоступаПриОбновленииИБ");
				ОбновлениеИнформационнойБазы.ЗаписатьДанные(Объект);
			Иначе
				Объект.ОбменДанными.Загрузка = Истина;
				// АПК:1327-выкл - №783.1.4.1 Допустимо оставить запись без
				// предварительной управляемой блокировки объекта, так как только
				// для RLS в формате БСП 2.х (устарело) и ни разу не вызывало проблем.
				Объект.Записать();
				// АПК:1327-вкл.
			КонецЕсли;
			ЕстьИзменения = Истина;
		КонецЕсли;
	КонецЕсли;
	
	// Обновление объекта не требуется или объект уже обновлен.
	ЗаписатьНаборыЗначенийДоступа(Объект, ЕстьИзменения, ОбновлениеИБ);
	
КонецПроцедуры

// Заполняет вспомогательные данные, ускоряющие работу шаблонов ограничений доступа.
//  Выполняется перед записью в регистр НаборыЗначенийДоступа.
//
// Параметры:
//  СсылкаНаОбъект - ЛюбаяСсылка - ссылка на объект для которого заполняются наборы значений доступа.
//  Таблица        - ТаблицаЗначений
//  ДобавитьКэшРеквизиты - Булево
//
Процедура ПодготовитьНаборыЗначенийДоступаКЗаписи(СсылкаНаОбъект, Таблица, ДобавитьКэшРеквизиты = Ложь) Экспорт
	
	Если ДобавитьКэшРеквизиты Тогда
		
		Таблица.Колонки.Добавить("Объект", Метаданные.РегистрыСведений.НаборыЗначенийДоступа.Измерения.Объект.Тип);
		Таблица.Колонки.Добавить("СтандартноеЗначение", Новый ОписаниеТипов("Булево"));
		Таблица.Колонки.Добавить("ЗначениеБезГрупп", Новый ОписаниеТипов("Булево"));
		
		СвойстваВидовДоступа = СвойстваВидовДоступа();
		
		ТипыЗначенийДоступаСГруппами = СвойстваВидовДоступа.ТипыЗначенийДоступаСГруппами;
		ТипыЗначенийДоступа          = СвойстваВидовДоступа.ПоТипамЗначений;
	КонецЕсли;
	
	// Нормализация ресурсов Чтение, Изменение.
	НомерНабора = -1;
	Для каждого Строка Из Таблица Цикл
		
		Если ДобавитьКэшРеквизиты Тогда
			// Установка значения измерения Объект.
			Строка.Объект = СсылкаНаОбъект;
			
			ТипЗначенияДоступа = ТипЗнч(Строка.ЗначениеДоступа);
			
			Если ТипыЗначенийДоступа.Получить(ТипЗначенияДоступа) <> Неопределено Тогда
				Строка.СтандартноеЗначение = Истина;
				Если ТипыЗначенийДоступаСГруппами.Получить(ТипЗначенияДоступа) = Неопределено Тогда
					Строка.ЗначениеБезГрупп = Истина;
				КонецЕсли;
			КонецЕсли;
			
		КонецЕсли;
		
		// Очистка флажков прав и соответствующих им вторичных данных
		// для всех строк каждого набора, кроме первой строки.
		Если НомерНабора = Строка.НомерНабора Тогда
			Строка.Чтение    = Ложь;
			Строка.Изменение = Ложь;
		Иначе
			НомерНабора = Строка.НомерНабора;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для действий при изменений настроек подсистемы.

// Если необходимо, включает заполнение данных для ограничения доступа и
// обновляет некоторые данные сразу.
//
// Вызывается из обработчика ПриЗаписи константы ОграничиватьДоступаНаУровнеЗаписей.
//
Процедура ПриИзмененииОграниченияДоступаНаУровнеЗаписей(ОграничениеДоступаНаУровнеЗаписейВключено) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	РегистрыСведений.ИспользуемыеВидыДоступа.ОбновитьДанныеРегистра(, Истина);
	РегистрыСведений.ИспользуемыеВидыДоступа.ПриИзмененииИспользованияВидовДоступа();
	
	Если ОграничениеДоступаНаУровнеЗаписейВключено Тогда
		ЗаписьЖурналаРегистрации(
			НСтр("ru = 'Управление доступом.Заполнение данных для ограничения доступа'",
			     ОбщегоНазначения.КодОсновногоЯзыка()),
			УровеньЖурналаРегистрации.Информация,
			,
			,
			НСтр("ru = 'Начато заполнение данных для ограничения доступа.'"),
			РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
		
		УстановитьЗаполнениеДанныхДляОграниченияДоступа(Истина);
	КонецЕсли;
	
	Если ОграничиватьДоступНаУровнеЗаписейУниверсально(Истина) Тогда
		
		ЗапланироватьОбновлениеПараметровОграниченияДоступа(
			"ПриИзмененииОграниченияДоступаНаУровнеЗаписей");
		
		ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа();
		ПараметрыПланирования.ЭтоПродолжениеОбновления = Истина;
		ПараметрыПланирования.Описание = "ОграничиватьДоступНаУровнеЗаписейПриЗаписи";
		ЗапланироватьОбновлениеДоступа(, ПараметрыПланирования);
		
		УстановитьОбновлениеДоступа(Истина);
	КонецЕсли;
	
	ОбновитьПараметрыСеанса();
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Обслуживание таблиц ВидыДоступа и ЗначенияДоступа в формах редактирования.

// Заполняет вспомогательные данные, требуемые для работы формы,
// которые не зависят от содержания объекта или заполняются для нового объекта.
//
// Форма должна содержать реквизиты, указанные ниже.
// Реквизиты отмеченные символом & заполняются автоматически, но их нужно создать в форме.
// Реквизиты отмеченные символом # должны быть созданы в форме, если
// в форме будет создан реквизит ТекущаяГруппаДоступа (см. ниже).
// Реквизиты отмеченные символом @ будут созданы автоматически.
//
//  ТекущаяГруппаДоступа - необязательный реквизит,
//                         если не создан в форме, тогда не используется.
//
//  ВидыДоступа - Таблица с полями:
//    #ГруппаДоступа              - СправочникСсылка.ГруппыДоступа,
//    ВидДоступа                  - ОпределяемыйТип.ЗначениеДоступа,
//    Предустановленный           - Булево (только для профиля),
//    ВсеРазрешены                - Булево,
//    &ВидДоступаПредставление    - Строка - представление настройки,
//    &ВсеРазрешеныПредставление  - Строка - представление настройки,
//    @Используется               - Булево.
//
//  ЗначенияДоступа - Таблица с полями:
//    #ГруппаДоступа     - СправочникСсылка.ГруппыДоступа,
//    &ВидДоступа        - ОпределяемыйТип.ЗначениеДоступа,
//    ЗначениеДоступа    - ОпределяемыйТип.ЗначениеДоступа,
//    &НомерСтрокиПоВиду - Число.
//
//  &ИспользоватьВнешнихПользователей     - Булево - реквизит будет создан, если нет в форме.
//  &НадписьВидДоступа                    - Строка - представление текущего вида доступа в форме.
//  @ЭтоПрофильГруппДоступа               - Булево.
//  @ТекущийВидДоступа                    - ОпределяемыйТип.ЗначениеДоступа.
//  @ТекущиеТипыВыбираемыхЗначений        - СписокЗначений.
//  @ТекущийТипВыбираемыхЗначений         - ОпределяемыйТип.ЗначениеДоступа.
//  @ИмяРеквизитаХранилищаТаблиц          - Строка.
//  @ВидДоступаПользователи               - ОпределяемыйТип.ЗначениеДоступа.
//  @ВидДоступаВнешниеПользователи        - ОпределяемыйТип.ЗначениеДоступа.
//  
//  @ВсеВидыДоступа - Таблица с полями:
//    @Ссылка        - ОпределяемыйТип.ЗначениеДоступа,
//    @Представление - Строка,
//    @Используется  - Булево.
//
//  @ПредставленияВсеРазрешены - Таблица с полями:
//    @Имя           - Строка,
//    @Представление - Строка.
//
//  @ВсеТипыВыбираемыхЗначений - Таблица с полями:
//    @ВидДоступа        - ОпределяемыйТип.ЗначениеДоступа,
//    @ТипЗначений       - ОпределяемыйТип.ЗначениеДоступа,
//    @ПредставлениеТипа - Строка,
//    @ИмяТаблицы        - Строка,
//    @ИерархияЭлементов - Булево.
//
// Параметры:
//  Форма      - см. УправлениеДоступомСлужебныйКлиентСервер.ПараметрыФормыРедактированияРазрешенныхЗначений
//
//  ЭтоПрофиль - Булево - указывает, что возможна настройка видов доступа
//               в том числе представление настройки содержит 4 значения, а не 2.
//
//  ИмяРеквизитаХранилищаТаблиц - Строка - содержащая, например, строку "Объект", которая
//               содержит таблицы ВидыДоступа и ЗначенияДоступа (см. ниже).
//               Если указана пустая строка, тогда считается,
//               что таблицы хранятся в реквизитах формы.
//
Процедура ПриСозданииНаСервереФормыРедактированияРазрешенныхЗначений(Форма, ЭтоПрофиль = Ложь, ИмяРеквизитаХранилищаТаблиц = "Объект") Экспорт
	
	ДобавитьРеквизитыВспомогательныхДанныхВФорму(Форма, ИмяРеквизитаХранилищаТаблиц);
	
	Форма.ИмяРеквизитаХранилищаТаблиц = ИмяРеквизитаХранилищаТаблиц;
	Форма.ЭтоПрофильГруппДоступа = ЭтоПрофиль;
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	
	// Заполнение типов значений доступа всех видов доступа.
	Для каждого СвойстваВидаДоступа Из СвойстваВидовДоступа.Массив Цикл
		Для каждого Тип Из СвойстваВидаДоступа.ТипыВыбираемыхЗначений Цикл
			МассивТипов = Новый Массив;
			МассивТипов.Добавить(Тип);
			ОписаниеТипа = Новый ОписаниеТипов(МассивТипов);
			
			МетаданныеТипа = Метаданные.НайтиПоТипу(Тип);
			Если Метаданные.Перечисления.Найти(МетаданныеТипа.Имя) = МетаданныеТипа Тогда
				ПредставлениеТипа = МетаданныеТипа.Представление();
			Иначе
				ПредставлениеТипа = ?(ЗначениеЗаполнено(МетаданныеТипа.ПредставлениеОбъекта),
					МетаданныеТипа.ПредставлениеОбъекта,
					МетаданныеТипа.Представление());
			КонецЕсли;
			
			НоваяСтрока = Форма.ВсеТипыВыбираемыхЗначений.Добавить();
			НоваяСтрока.ВидДоступа        = СвойстваВидаДоступа.Ссылка;
			НоваяСтрока.ТипЗначений       = ОписаниеТипа.ПривестиЗначение(Неопределено);
			НоваяСтрока.ПредставлениеТипа = ПредставлениеТипа;
			НоваяСтрока.ИмяТаблицы        = МетаданныеТипа.ПолноеИмя();
			НоваяСтрока.ИерархияЭлементов = СвойстваВидовДоступа.ПоТипамЗначенийСИерархией.Получить(Тип) <> Неопределено;
		КонецЦикла;
	КонецЦикла;
	
	Форма.ВидДоступаПользователи           = Справочники.Пользователи.ПустаяСсылка();
	Форма.ВидДоступаВнешниеПользователи    = Справочники.ВнешниеПользователи.ПустаяСсылка();
	Форма.ИспользоватьВнешнихПользователей = ВнешниеПользователи.ИспользоватьВнешнихПользователей();
	
	ЗаполнитьТаблицуВсеВидыДоступаВФорме(Форма);
	
	ЗаполнитьТаблицуПредставленияВсеРазрешеныВФорме(Форма, ЭтоПрофиль);
	
	ОформитьТаблицуВидыДоступаВФорме(Форма);
	
	ОформитьТаблицуЗначенияДоступаВФорме(Форма);
	
	УдалитьНесуществующиеВидыИЗначенияДоступа(Форма);
	УправлениеДоступомСлужебныйКлиентСервер.ЗаполнитьСвойстваВидовДоступаВФорме(Форма);
	
	ОбновитьОтображениеНеиспользуемыхВидовДоступа(Форма, Истина);
	
	// Настройка параметров выбора значения доступа.
	ПараметрыВыбора = Новый Массив;
	ПараметрыВыбора.Добавить(Новый ПараметрВыбора("ЭтоВыборЗначенияДоступа", Истина));
	Форма.Элементы.ЗначенияДоступаЗначениеДоступа.ПараметрыВыбора = Новый ФиксированныйМассив(ПараметрыВыбора);
	
КонецПроцедуры

// При повторном чтении заполняет или обновляет вспомогательные данные,
// требуемые для работы формы, которые зависят от содержания объекта.
//
Процедура ПриПовторномЧтенииНаСервереФормыРедактированияРазрешенныхЗначений(Форма, ТекущийОбъект) Экспорт
	
	УдалитьНесуществующиеВидыИЗначенияДоступа(Форма, ТекущийОбъект);
	УдалитьНесуществующиеВидыИЗначенияДоступа(Форма);
	
	УправлениеДоступомСлужебныйКлиентСервер.ЗаполнитьСвойстваВидовДоступаВФорме(Форма);
	
	УправлениеДоступомСлужебныйКлиентСервер.ПриИзмененииТекущегоВидаДоступа(Форма, Ложь);
	
КонецПроцедуры

// Удаляет лишние значения доступа перед записью.
// Лишние значения доступа могут появиться, если заменить или удалить вид доступа,
// для которого введены значения доступа.
//
Процедура ПередЗаписьюНаСервереФормыРедактированияРазрешенныхЗначений(Форма, ТекущийОбъект) Экспорт
	
	УдалитьЛишниеЗначенияДоступа(Форма, ТекущийОбъект);
	УдалитьЛишниеЗначенияДоступа(Форма);
	
КонецПроцедуры

// Обновляет свойства видов доступа.
Процедура ПослеЗаписиНаСервереФормыРедактированияРазрешенныхЗначений(Форма, ТекущийОбъект, ПараметрыЗаписи) Экспорт
	
	УдалитьНесуществующиеВидыИЗначенияДоступа(Форма, ТекущийОбъект);
	УдалитьНесуществующиеВидыИЗначенияДоступа(Форма);
	
	УправлениеДоступомСлужебныйКлиентСервер.ЗаполнитьСвойстваВидовДоступаВФорме(Форма);
	
КонецПроцедуры

// Скрывает или показывает неиспользуемые виды доступа.
Процедура ОбновитьОтображениеНеиспользуемыхВидовДоступа(Форма, ПриСозданииНаСервере = Ложь) Экспорт
	
	Элементы = Форма.Элементы;
	
	Если Не ПриСозданииНаСервере Тогда
		Элементы.ПоказыватьНеИспользуемыеВидыДоступа.Пометка =
			НЕ Элементы.ПоказыватьНеИспользуемыеВидыДоступа.Пометка;
	КонецЕсли;
	
	Отбор = УправлениеДоступомСлужебныйКлиентСервер.ОтборВТаблицахФормыРедактированияРазрешенныхЗначений(
		Форма);
	
	Если Не Элементы.ПоказыватьНеиспользуемыеВидыДоступа.Пометка Тогда
		Отбор.Вставить("Используется", Истина);
	КонецЕсли;
	
	Элементы.ВидыДоступа.ОтборСтрок = Новый ФиксированнаяСтруктура(Отбор);
	
	Элементы.ВидыДоступаВидДоступаПредставление.СписокВыбора.Очистить();
	
	Для каждого Строка Из Форма.ВсеВидыДоступа Цикл
		
		Если Не Элементы.ПоказыватьНеиспользуемыеВидыДоступа.Пометка
		   И Не Строка.Используется Тогда
			
			Продолжить;
		КонецЕсли;
		
		Элементы.ВидыДоступаВидДоступаПредставление.СписокВыбора.Добавить(Строка.Представление);
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Универсальные процедуры и функции.

// Только для внутреннего использования.
Процедура УстановитьУсловиеОтбораВЗапросе(Знач Запрос, Знач Значения, Знач ИмяПараметраЗначений, Знач ИмяПараметраУсловияОтбораИмяПоля) Экспорт
	
	Если Значения = Неопределено Тогда
		
	ИначеЕсли ТипЗнч(Значения) <> Тип("Массив")
	        И ТипЗнч(Значения) <> Тип("ФиксированныйМассив") Тогда
		
		Запрос.УстановитьПараметр(ИмяПараметраЗначений, Значения);
		
	ИначеЕсли Значения.Количество() = 1 Тогда
		Запрос.УстановитьПараметр(ИмяПараметраЗначений, Значения[0]);
	Иначе
		Запрос.УстановитьПараметр(ИмяПараметраЗначений, Значения);
	КонецЕсли;
	
	Для НомерСтроки = 1 По СтрЧислоСтрок(ИмяПараметраУсловияОтбораИмяПоля) Цикл
		ТекущаяСтрока = СтрПолучитьСтроку(ИмяПараметраУсловияОтбораИмяПоля, НомерСтроки);
		Если НЕ ЗначениеЗаполнено(ТекущаяСтрока) Тогда
			Продолжить;
		КонецЕсли;
		ИндексРазделителя = СтрНайти(ТекущаяСтрока, ":");
		Если ИндексРазделителя = 0 Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка при выполнении процедуры %1.
				           |
				           |В параметре %2 не существует разделитель (двоеточие)
				           |в следующей строке формата ""%3""
				           |""%4"".'"),
				"УправлениеДоступом.УстановитьУсловиеОтбораВЗапросе",
				"ИмяПараметраУсловияОтбораИмяПоля",
				"<ИмяПараметраУсловия>:<ИмяПоля>",
				ТекущаяСтрока);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ИмяПараметраУсловияОтбора = Лев(ТекущаяСтрока, ИндексРазделителя-1);
		ИмяПоля = Сред(ТекущаяСтрока, ИндексРазделителя+1);
		Если Значения = Неопределено Тогда
			УсловиеОтбора = "Истина";
			
		ИначеЕсли ТипЗнч(Значения) <> Тип("Массив")
		        И ТипЗнч(Значения) <> Тип("ФиксированныйМассив") Тогда
			
			УсловиеОтбора = ИмяПоля + " = &" + ИмяПараметраЗначений;
			
		ИначеЕсли Значения.Количество() = 1 Тогда
			УсловиеОтбора = ИмяПоля + " = &" + ИмяПараметраЗначений;
		Иначе
			УсловиеОтбора = ИмяПоля + " В (&" + ИмяПараметраЗначений + ")";
		КонецЕсли;
		Запрос.Текст = СтрЗаменить(Запрос.Текст, ИмяПараметраУсловияОтбора, УсловиеОтбора);
	КонецЦикла;
	
КонецПроцедуры

// Обновляет набор записей в базе данных,
// если записи набора отличаются от записей в базе данных.
//
// Параметры:
//  Данные - Структура:
//    * НаборЗаписей           - РегистрСведенийНаборЗаписей - пустой или прочитанный с заданным отбором или без.
//                             - РегистрСведенийМенеджер - менеджер регистра для создания набора записей.
//
//    * НовыеЗаписи            - ТаблицаЗначений - в формате регистра.
//
//    * ПоляСравнения          - Строка - содержит список полей по значениям которых требуется вычислять
//                               отличие записей набора. Например, "Измерение1, Измерение2, Ресурс1",
//                               а реквизит ДатаИзмерения не входит в список.
//
//    * ПолеОтбора             - Неопределено - записывается весь регистр или
//                                              отбор уже задан в наборе записей.
//                               Строка       - имя поля по которому нужно установить отбор.
//
//    * ЗначениеОтбора         - Отбор - значение, которое будет установлено в качестве отбора
//                               по полю отбора, если поле отбора задано.
//
//    * НаборЗаписейПрочитан   - Булево - если Истина, тогда не заданный набор записей уже содержит
//                               прочитанные записи блокировка данных этих записей установлена и
//                               транзакция открыта.
//
//    * ТолькоПроверка         - Булево - если Истина, тогда не выполнять запись,
//                               а лишь выявить необходимость записи и установить
//                               свойство ЕстьИзменения.
//
//    * ДополнительныеСвойства - Структура
//                             - Неопределено - если Структура, тогда в
//                               объекты <Регистр*>НаборЗаписей в свойство ДополнительныеСвойства
//                               будут вставлены все параметры структуры.
//
//    * ОбновлениеИБ           - Булево - если Истина, то необходимо выполнять запись данных,
//                               не выполняя лишних, избыточных действий с данными.
//                               См. ОбновлениеИнформационнойБазы.ЗаписатьДанные.
//                               Если свойство не вставлено, то значение вычисляется по "Или" с помощью функций
//                               ВыполняетсяОбновлениеИнформационнойБазы и ЭтоВызовИзОбработчикаОбновления
//                               общего модуля ОбновлениеИнформационнойБазы.
//
//  ЕстьИзменения         - Булево - возвращаемое значение. Если производилась запись,
//                          устанавливается Истина, иначе не изменяется.
//
//  ИзмененныеЗаписи      - Неопределено - никаких действий, иначе
//                          возвращает таблицу значений в формате регистра с полем ВидИзмененияСтроки
//                          типа Число (-1 запись удалена, 1 запись добавлена).
//
Процедура ОбновитьНаборЗаписей(Знач Данные, ЕстьИзменения = Неопределено, ИзмененныеЗаписи = Неопределено) Экспорт
	
	ВсеПараметры = Новый Структура;
	ВсеПараметры.Вставить("НаборЗаписей");
	ВсеПараметры.Вставить("НовыеЗаписи");
	ВсеПараметры.Вставить("ПоляСравнения");
	ВсеПараметры.Вставить("ПолеОтбора");
	ВсеПараметры.Вставить("ЗначениеОтбора");
	ВсеПараметры.Вставить("НаборЗаписейПрочитан", Ложь);
	ВсеПараметры.Вставить("БезПерезаписи", Ложь);
	ВсеПараметры.Вставить("ТолькоПроверка", Ложь);
	ВсеПараметры.Вставить("ДополнительныеСвойства");
	ВсеПараметры.Вставить("ОбновлениеИБ",
		    ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		Или ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления());
	
	ЗаполнитьПараметры(Данные, ВсеПараметры, "НаборЗаписей, НовыеЗаписи");
	
	ПолноеИмяРегистра = Метаданные.НайтиПоТипу(ТипЗнч(Данные.НаборЗаписей)).ПолноеИмя();
	МенеджерРегистра = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмяРегистра);
	Если Данные.НаборЗаписей = МенеджерРегистра Тогда
		НаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей(); // РегистрСведенийНаборЗаписей
	Иначе
		НаборЗаписей = Данные.НаборЗаписей; // РегистрСведенийНаборЗаписей
	КонецЕсли;
	НовыеЗаписи    = Данные.НовыеЗаписи;
	ПолеОтбора     = Данные.ПолеОтбора;
	ЗначениеОтбора = Данные.ЗначениеОтбора;
	
	Если ЗначениеЗаполнено(ПолеОтбора) Тогда
		УстановитьОтбор(НаборЗаписей.Отбор[ПолеОтбора], ЗначениеОтбора);
	КонецЕсли;
	
	Если НЕ Данные.НаборЗаписейПрочитан Тогда
		ЗаблокироватьОбластьНабораЗаписей(НаборЗаписей, ПолноеИмяРегистра);
		НаборЗаписей.Прочитать();
	КонецЕсли;
	
	Данные.ПоляСравнения = ?(Данные.ПоляСравнения = Неопределено,
		ПоляНабораЗаписей(НаборЗаписей), Данные.ПоляСравнения);
	
	Если Данные.БезПерезаписи Тогда
		НаборЗаписи = МенеджерРегистра.СоздатьНаборЗаписей();
		ОписаниеКлючаЗаписи = УправлениеДоступомСлужебныйПовтИсп.ОписаниеКлючаЗаписи(ПолноеИмяРегистра);
		ОтборЗаписи = Новый Структура(ОписаниеКлючаЗаписи.СтрокаПолей);
		ПоляОстальныхИзмерений = Новый Массив;
		Для каждого Поле Из ОписаниеКлючаЗаписи.МассивПолей Цикл
			Если Поле <> ПолеОтбора Тогда
				ПоляОстальныхИзмерений.Добавить(Поле);
			КонецЕсли;
		КонецЦикла;
		УдаляемыеЗаписи = Новый ТаблицаЗначений;
		Для каждого Поле Из ПоляОстальныхИзмерений Цикл
			УдаляемыеЗаписи.Колонки.Добавить(Поле);
		КонецЦикла;
		Данные.НовыеЗаписи = НовыеЗаписи.Скопировать();
		НовыеЗаписи = Данные.НовыеЗаписи;
	КонецЕсли;
	
	ЕстьТекущиеИзменения = Ложь;
	Если ИзмененныеЗаписи = Неопределено Тогда
		Если НаборЗаписей.Количество() = НовыеЗаписи.Количество() ИЛИ Данные.БезПерезаписи Тогда
			Отбор = Новый Структура(Данные.ПоляСравнения);
			НовыеЗаписи.Индексы.Добавить(Данные.ПоляСравнения);
			Для Каждого Запись Из НаборЗаписей Цикл
				ЗаполнитьЗначенияСвойств(Отбор, Запись);
				НайденныеСтроки = НовыеЗаписи.НайтиСтроки(Отбор);
				Если НайденныеСтроки.Количество() = 0 Тогда
					ЕстьТекущиеИзменения = Истина;
					ЕстьИзменения = Истина;
					Если Данные.БезПерезаписи Тогда
						ЗаполнитьЗначенияСвойств(ОтборЗаписи, Запись);
						Если НовыеЗаписи.НайтиСтроки(ОтборЗаписи).Количество() = 0 Тогда
							ЗаполнитьЗначенияСвойств(УдаляемыеЗаписи.Добавить(), ОтборЗаписи);
						КонецЕсли;
					Иначе
						Прервать;
					КонецЕсли;
				ИначеЕсли Данные.БезПерезаписи Тогда
					НовыеЗаписи.Удалить(НайденныеСтроки[0]);
				КонецЕсли;
			КонецЦикла;
			Если Данные.БезПерезаписи И НовыеЗаписи.Количество() > 0 Тогда
				ЕстьТекущиеИзменения = Истина;
				ЕстьИзменения = Истина;
			КонецЕсли;
		Иначе
			ЕстьТекущиеИзменения = Истина;
			ЕстьИзменения = Истина;
		КонецЕсли;
	Иначе
		Если НаборЗаписей.Количество() <> НовыеЗаписи.Количество() Тогда
			ЕстьТекущиеИзменения = Истина;
			ЕстьИзменения = Истина;
		КонецЕсли;
		Если НаборЗаписей.Количество() > НовыеЗаписи.Количество() Тогда
			ИзмененныеЗаписи = НаборЗаписей.Выгрузить();
			ИскомыеЗаписи   = НовыеЗаписи;
			ВидИзмененияСтроки = -1;
		Иначе
			ИзмененныеЗаписи = НовыеЗаписи.Скопировать();
			ИскомыеЗаписи   = НаборЗаписей.Выгрузить();
			ВидИзмененияСтроки = 1;
		КонецЕсли;
		ИзмененныеЗаписи.Колонки.Добавить("ВидИзмененияСтроки", Новый ОписаниеТипов("Число"));
		ИзмененныеЗаписи.ЗаполнитьЗначения(ВидИзмененияСтроки, "ВидИзмененияСтроки");
		ВидИзмененияСтроки = ?(ВидИзмененияСтроки = 1, -1, 1);
		Отбор = Новый Структура(Данные.ПоляСравнения);
		
		Для каждого Строка Из ИскомыеЗаписи Цикл
			ЗаполнитьЗначенияСвойств(Отбор, Строка);
			Строки = ИзмененныеЗаписи.НайтиСтроки(Отбор);
			Если Строки.Количество() = 0 Тогда
				НоваяСтрока = ИзмененныеЗаписи.Добавить();
				ЗаполнитьЗначенияСвойств(НоваяСтрока, Отбор);
				НоваяСтрока.ВидИзмененияСтроки = ВидИзмененияСтроки;
				ЕстьТекущиеИзменения = Истина;
				ЕстьИзменения = Истина;
			Иначе
				ИзмененныеЗаписи.Удалить(Строки[0]);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если ЕстьТекущиеИзменения Тогда
		Если Данные.ТолькоПроверка Тогда
			Возврат;
		КонецЕсли;
		Если Данные.БезПерезаписи Тогда
			УстановитьДополнительныеСвойства(НаборЗаписи, Данные.ДополнительныеСвойства);
			Для каждого Строка Из УдаляемыеЗаписи Цикл
				Если ЗначениеЗаполнено(ПолеОтбора) Тогда
					УстановитьОтбор(НаборЗаписи.Отбор[ПолеОтбора], ЗначениеОтбора);
				КонецЕсли;
				Для каждого Поле Из ПоляОстальныхИзмерений Цикл
					УстановитьОтбор(НаборЗаписи.Отбор[Поле], Строка[Поле]);
				КонецЦикла;
				ЗаписатьОбъектИлиНаборЗаписей(Данные, НаборЗаписи);
			КонецЦикла;
			НаборЗаписи.Добавить();
			Для каждого Строка Из НовыеЗаписи Цикл
				Если ЗначениеЗаполнено(ПолеОтбора) Тогда
					УстановитьОтбор(НаборЗаписи.Отбор[ПолеОтбора], ЗначениеОтбора);
				КонецЕсли;
				Для каждого Поле Из ПоляОстальныхИзмерений Цикл
					УстановитьОтбор(НаборЗаписи.Отбор[Поле], Строка[Поле]);
				КонецЦикла;
				ЗаполнитьЗначенияСвойств(НаборЗаписи[0], Строка);
				ЗаписатьОбъектИлиНаборЗаписей(Данные, НаборЗаписи);
			КонецЦикла;
		Иначе
			УстановитьДополнительныеСвойства(НаборЗаписей, Данные.ДополнительныеСвойства);
			НаборЗаписей.Загрузить(НовыеЗаписи);
			ЗаписатьОбъектИлиНаборЗаписей(Данные, НаборЗаписей);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Обновляет строки регистра с отбором по нескольким значениям для одного или
// для двух измерений регистра, выполняется проверка наличия изменений,
// если изменений нет, перезапись не производится.
//
// Параметры:
//  Данные - Структура:
//    * МенеджерРегистра          - РегистрСведенийМенеджер - менеджер регистра для создания набора записей.
//
//    * НовыеЗаписи               - ТаблицаЗначений - в формате регистра.
//
//    * ПоляСравнения             - Строка - содержит список полей по значениям которых требуется
//                                  вычислять отличие записей набора, например, "Измерение1, Измерение2,
//                                  Ресурс1", а реквизит ДатаИзменения не входит в список.
//
//    * ИмяПервогоИзмерения       - Неопределено - нет отбора по измерению.
//                                - Строка       - содержит имя первого измерения, для которого задано
//                                                 несколько значений.
//                                
//    * ЗначенияПервогоИзмерения  - Неопределено - нет отбора по измерению, аналогично,
//                                                 ИмяПервогоИзмерения = Неопределено.
//                                - ЛюбаяСсылка  - содержит одно значение отбора регистра для
//                                                 обновляемых записей.
//                                - Массив       - содержит массив значений отбора регистра для
//                                                 обновляемых записей, пустой массив - значит
//                                                 действий не требуется.
//
//    * ИмяВторогоИзмерения       - Неопределено
//                                - Строка - аналогично ИмяПервогоИзмерения.
//    * ЗначенияВторогоИзмерения  - Неопределено
//                                - ЛюбаяСсылка
//                                - Массив - аналогично ЗначенияПервогоИзмерения.
//    * ИмяТретьегоИзмерения      - Неопределено
//                                - Строка - аналогично ИмяПервогоИзмерения.
//    * ЗначенияТретьегоИзмерения - Неопределено
//                                - ЛюбаяСсылка
//                                - Массив - аналогично ЗначенияПервогоИзмерения.
//
//    * ТолькоПроверка            - Булево - если Истина, тогда не выполнять запись,
//                                  а лишь выявить необходимость записи и установить
//                                  свойство ЕстьИзменения.
//
//    * ДополнительныеСвойства    - Неопределено
//                                - Структура - если Структура, тогда в
//                                  объекты <Регистр*>НаборЗаписей в свойство
//                                  ДополнительныеСвойства будут вставлены все параметры структуры.
//
//    * ОбновлениеИБ              - Булево - если Истина, то необходимо выполнять запись данных,
//                                  не выполняя лишних, избыточных действий с данными.
//                                  См. ОбновлениеИнформационнойБазы.ЗаписатьДанные.
//                                  Если свойство не вставлено, то значение вычисляется по "Или" с помощью функций
//                                  ВыполняетсяОбновлениеИнформационнойБазы и ЭтоВызовИзОбработчикаОбновления
//                                  общего модуля ОбновлениеИнформационнойБазы.
//
//  ЕстьИзменения             - Булево - возвращаемое значение. Если производилась запись,
//                              устанавливается Истина, иначе не изменяется.
//
Процедура ОбновитьНаборыЗаписей(Знач Данные, ЕстьИзменения) Экспорт
	
	ВсеПараметры = Новый Структура;
	ВсеПараметры.Вставить("МенеджерРегистра");
	ВсеПараметры.Вставить("НовыеЗаписи");
	ВсеПараметры.Вставить("ПоляСравнения");
	ВсеПараметры.Вставить("ИмяПервогоИзмерения");
	ВсеПараметры.Вставить("ЗначенияПервогоИзмерения");
	ВсеПараметры.Вставить("ИмяВторогоИзмерения");
	ВсеПараметры.Вставить("ЗначенияВторогоИзмерения");
	ВсеПараметры.Вставить("ИмяТретьегоИзмерения");
	ВсеПараметры.Вставить("ЗначенияТретьегоИзмерения");
	ВсеПараметры.Вставить("НовыеЗаписиСодержатТолькоРазличия", Ложь);
	ВсеПараметры.Вставить("ФиксированныйОтбор");
	ВсеПараметры.Вставить("ТолькоПроверка", Ложь);
	ВсеПараметры.Вставить("ДополнительныеСвойства");
	ВсеПараметры.Вставить("ОбновлениеИБ",
		    ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		Или ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления());
	
	ЗаполнитьПараметры(Данные, ВсеПараметры, "МенеджерРегистра, НовыеЗаписи");
	
	// Предварительная обработка параметров.
	
	Если НЕ ГруппаПараметровИзмеренияОбработана(Данные.ИмяПервогоИзмерения, Данные.ЗначенияПервогоИзмерения) Тогда
		ЕстьИзменения = Истина;
		Возврат;
	КонецЕсли;
	Если НЕ ГруппаПараметровИзмеренияОбработана(Данные.ИмяВторогоИзмерения, Данные.ЗначенияВторогоИзмерения) Тогда
		ЕстьИзменения = Истина;
		Возврат;
	КонецЕсли;
	Если НЕ ГруппаПараметровИзмеренияОбработана(Данные.ИмяТретьегоИзмерения, Данные.ЗначенияТретьегоИзмерения) Тогда
		ЕстьИзменения = Истина;
		Возврат;
	КонецЕсли;
	
	УпорядочитьГруппыПараметровИзмерений(Данные);
	
	// Проверка и обновление данных.
	Данные.Вставить("НаборЗаписей",       Данные.МенеджерРегистра.СоздатьНаборЗаписей());
	Данные.Вставить("МетаданныеРегистра", Метаданные.НайтиПоТипу(ТипЗнч(Данные.НаборЗаписей)));
	Данные.Вставить("ПолноеИмяРегистра",  Данные.МетаданныеРегистра.ПолноеИмя());
	
	Если Данные.НовыеЗаписиСодержатТолькоРазличия Тогда
		Данные.Вставить("НаборДляОднойЗаписи", Данные.МенеджерРегистра.СоздатьНаборЗаписей());
	КонецЕсли;
	
	Если Данные.ФиксированныйОтбор <> Неопределено Тогда
		Для каждого КлючИЗначение Из Данные.ФиксированныйОтбор Цикл
			УстановитьОтбор(Данные.НаборЗаписей.Отбор[КлючИЗначение.Ключ], КлючИЗначение.Значение);
		КонецЦикла;
	КонецЕсли;
	
	Если Данные.НовыеЗаписиСодержатТолькоРазличия Тогда
		
		Если Данные.ИмяПервогоИзмерения = Неопределено Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Некорректные параметры в процедуре %1.'"),
				"ОбновитьНаборыЗаписей");
			ВызватьИсключение ТекстОшибки;
		Иначе
			Если Данные.ИмяВторогоИзмерения = Неопределено Тогда
				ЗаписьНесколькимиНаборами = Ложь;
			Иначе
				ЗаписьНесколькимиНаборами = ЗаписьНесколькимиНаборами(Данные,
					Новый Структура, Данные.ИмяПервогоИзмерения, Данные.ЗначенияПервогоИзмерения);
			КонецЕсли;
			
			Если ЗаписьНесколькимиНаборами Тогда
				СписокПолей = Данные.ИмяПервогоИзмерения + ", " + Данные.ИмяВторогоИзмерения;
				Данные.НовыеЗаписи.Индексы.Добавить(СписокПолей);
				
				КоличествоПоЗначениямПервогоИзмерения = Данные.КоличествоПоЗначениям;
				
				Для каждого ПервоеЗначение Из Данные.ЗначенияПервогоИзмерения Цикл
					Отбор = Новый Структура(Данные.ИмяПервогоИзмерения, ПервоеЗначение);
					УстановитьОтбор(Данные.НаборЗаписей.Отбор[Данные.ИмяПервогоИзмерения], ПервоеЗначение);
					
					Если Данные.ИмяТретьегоИзмерения = Неопределено Тогда
						ЗаписьНесколькимиНаборами = Ложь;
					Иначе
						// @skip-check query-in-loop - Порционная обработка данных
						ЗаписьНесколькимиНаборами = ЗаписьНесколькимиНаборами(Данные,
							Отбор, Данные.ИмяВторогоИзмерения, Данные.ЗначенияВторогоИзмерения);
					КонецЕсли;
					
					Если ЗаписьНесколькимиНаборами Тогда
						Для каждого ВтороеЗначение Из Данные.ЗначенияВторогоИзмерения Цикл
							Отбор.Вставить(Данные.ИмяВторогоИзмерения, ВтороеЗначение);
							УстановитьОтбор(Данные.НаборЗаписей.Отбор[Данные.ИмяВторогоИзмерения], ВтороеЗначение);
							
							// Обновление по трем измерениям.
							ОбновитьНовыеЗаписиНабораПоРазличнымНовымЗаписям(Данные, Отбор, ЕстьИзменения);
						КонецЦикла;
						Данные.НаборЗаписей.Отбор[Данные.ИмяВторогоИзмерения].Использование = Ложь;
					Иначе
						// Обновление по двум измерениям.
						Данные.Вставить("КоличествоПоЗначениям", КоличествоПоЗначениямПервогоИзмерения);
						ОбновитьНовыеЗаписиНабораПоРазличнымНовымЗаписям(Данные, Отбор, ЕстьИзменения);
					КонецЕсли;
				КонецЦикла;
			Иначе
				// Обновление по одному измерению.
				ПрочитатьКоличествоДляЧтения(Данные);
				ОбновитьНовыеЗаписиНабораПоРазличнымНовымЗаписям(Данные, Новый Структура, ЕстьИзменения);
			КонецЕсли;
		КонецЕсли;
	Иначе
		Если Данные.ИмяПервогоИзмерения = Неопределено Тогда
			// Обновление всех записей.
			
			ТекущиеДанные = Новый Структура("НаборЗаписей, НовыеЗаписи, ПоляСравнения,
				|ТолькоПроверка, ДополнительныеСвойства, ОбновлениеИБ");
			ЗаполнитьЗначенияСвойств(ТекущиеДанные, Данные);
			ОбновитьНаборЗаписей(ТекущиеДанные, ЕстьИзменения);
			
		ИначеЕсли Данные.ИмяВторогоИзмерения = Неопределено Тогда
			// Обновление по одному измерению.
			Отбор = Новый Структура(Данные.ИмяПервогоИзмерения);
			Для каждого Значение Из Данные.ЗначенияПервогоИзмерения Цикл
				
				УстановитьОтбор(Данные.НаборЗаписей.Отбор[Данные.ИмяПервогоИзмерения], Значение);
				Отбор[Данные.ИмяПервогоИзмерения] = Значение;
				
				Если Данные.ЗначенияПервогоИзмерения.Количество() <> 1 Тогда
					НовыеЗаписиНабора = Данные.НовыеЗаписи;
				Иначе
					НовыеЗаписиНабора = Данные.НовыеЗаписи.Скопировать(Отбор);
				КонецЕсли;
				
				ТекущиеДанные = Новый Структура("НаборЗаписей, ПоляСравнения,
					|ТолькоПроверка, ДополнительныеСвойства, ОбновлениеИБ");
				ЗаполнитьЗначенияСвойств(ТекущиеДанные, Данные);
				ТекущиеДанные.Вставить("НовыеЗаписи", НовыеЗаписиНабора);
				
				ОбновитьНаборЗаписей(ТекущиеДанные, ЕстьИзменения);
			КонецЦикла;
			
		ИначеЕсли Данные.ИмяТретьегоИзмерения = Неопределено Тогда
			// Обновление по двум измерениям.
			СписокПолей = Данные.ИмяПервогоИзмерения + ", " + Данные.ИмяВторогоИзмерения;
			Данные.НовыеЗаписи.Индексы.Добавить(СписокПолей);
			Отбор = Новый Структура(СписокПолей);
			
			Для каждого ПервоеЗначение Из Данные.ЗначенияПервогоИзмерения Цикл
				УстановитьОтбор(Данные.НаборЗаписей.Отбор[Данные.ИмяПервогоИзмерения], ПервоеЗначение);
				Отбор[Данные.ИмяПервогоИзмерения] = ПервоеЗначение;
				
				ОбновитьНовыеЗаписиНабораПоВсемНовымЗаписям(
					Данные,
					Отбор,
					СписокПолей,
					Данные.ИмяВторогоИзмерения,
					Данные.ЗначенияВторогоИзмерения,
					ЕстьИзменения);
			КонецЦикла;
		Иначе
			// Обновление по трем измерениям.
			СписокПолей = Данные.ИмяПервогоИзмерения + ", " + Данные.ИмяВторогоИзмерения + ", " + Данные.ИмяТретьегоИзмерения;
			Данные.НовыеЗаписи.Индексы.Добавить(СписокПолей);
			Отбор = Новый Структура(СписокПолей);
			
			Для каждого ПервоеЗначение Из Данные.ЗначенияПервогоИзмерения Цикл
				УстановитьОтбор(Данные.НаборЗаписей.Отбор[Данные.ИмяПервогоИзмерения], ПервоеЗначение);
				Отбор[Данные.ИмяПервогоИзмерения] = ПервоеЗначение;
				
				Для каждого ВтороеЗначение Из Данные.ЗначенияВторогоИзмерения Цикл
					УстановитьОтбор(Данные.НаборЗаписей.Отбор[Данные.ИмяВторогоИзмерения], ВтороеЗначение);
					Отбор[Данные.ИмяВторогоИзмерения] = ВтороеЗначение;
					
					ОбновитьНовыеЗаписиНабораПоВсемНовымЗаписям(
						Данные,
						Отбор,
						СписокПолей,
						Данные.ИмяВторогоИзмерения,
						Данные.ЗначенияВторогоИзмерения,
						ЕстьИзменения);
				КонецЦикла;
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Обновляет регистр сведений по данным в таблице значений ИзмененияСтрок.
//
// Параметры:
//  Данные - Структура:
//
//  * МенеджерРегистра       - РегистрСведенийМенеджер - менеджер регистра для создания набора записей.
//
//  * ИзмененияСоставаСтрок  - ТаблицаЗначений - содержащей поля регистра и
//                             поле ВидИзмененияСтроки (Число):
//                               " 1" - значит, что строку нужно добавить,
//                               "-1" - значит, что строку нужно удалить.
//
//  * ФиксированныйОтбор     - Структура - содержащая имя измерения в ключе и значение
//                             отбора в значении. Может быть указана, когда измерений
//                             более 3-х и заранее известно, что по измерениям сверх 3-х
//                             будет единственное значение. Измерения указанные в
//                             фиксированном отборе не используются при формировании
//                             наборов записей для выполнения обновления.
//
//  * ИзмеренияОтбора        - Строка - измерений перечисленных через запятую, которые
//                             нужно использовать при формировании наборов записей
//                             для выполнения обновления (не более 3-х). Не указанные
//                             измерения будут превращены в фиксированный отбор,
//                             если по ним все значения совпадают.
//
//  * ТолькоПроверка         - Булево - если Истина, тогда не выполнять запись,
//                             а лишь выявить необходимость записи и установить
//                             свойство ЕстьИзменения.
//
//  * ДополнительныеСвойства - Неопределено
//                           - Структура - если Структура, тогда в
//                             объекты <Регистр*>НаборЗаписей в свойство
//                             ДополнительныеСвойства будут вставлены все параметры структуры.
//
//  * ОбновлениеИБ           - Булево - если Истина, то необходимо выполнять запись данных,
//                             не выполняя лишних, избыточных действий с данными.
//                             См. ОбновлениеИнформационнойБазы.ЗаписатьДанные.
//                             Если свойство не вставлено, то значение вычисляется по "Или" с помощью функций
//                             ВыполняетсяОбновлениеИнформационнойБазы и ЭтоВызовИзОбработчикаОбновления
//                             общего модуля ОбновлениеИнформационнойБазы.
//
//  ЕстьИзменения         - Булево - возвращаемое значение. Если производилась запись,
//                          устанавливается Истина, иначе не изменяется.
//
Процедура ОбновитьРегистрСведений(Знач Данные, ЕстьИзменения = Неопределено) Экспорт
	
	Если Данные.ИзмененияСоставаСтрок.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ВсеПараметры = Новый Структура;
	ВсеПараметры.Вставить("МенеджерРегистра");
	ВсеПараметры.Вставить("ИзмененияСоставаСтрок");
	ВсеПараметры.Вставить("ФиксированныйОтбор", Новый Структура);
	ВсеПараметры.Вставить("ИзмеренияОтбора");
	ВсеПараметры.Вставить("ТолькоПроверка", Ложь);
	ВсеПараметры.Вставить("ДополнительныеСвойства");
	ВсеПараметры.Вставить("ОбновлениеИБ",
		    ОбновлениеИнформационнойБазы.ВыполняетсяОбновлениеИнформационнойБазы()
		Или ОбновлениеИнформационнойБазы.ЭтоВызовИзОбработчикаОбновления());
	
	ЗаполнитьПараметры(Данные, ВсеПараметры, "МенеджерРегистра, ИзмененияСоставаСтрок");
	
	МетаданныеРегистра = Метаданные.НайтиПоТипу(ТипЗнч(Данные.МенеджерРегистра.ПустойКлюч()));
	ОписаниеКлючаЗаписи = УправлениеДоступомСлужебныйПовтИсп.ОписаниеКлючаЗаписи(МетаданныеРегистра.ПолноеИмя());
	
	Если Данные.ИзмеренияОтбора <> Неопределено Тогда
		Данные.ИзмеренияОтбора = Новый Структура(Данные.ИзмеренияОтбора);
	КонецЕсли;
	
	МассивИзмеренийОтбора   = Новый Массив;
	ЗначенияИзмеренийОтбора = Новый Структура;
	ИзмерениеБезФиксированногоОтбора = Новый Структура;
	
	Для Каждого Поле Из ОписаниеКлючаЗаписи.МассивПолей Цикл
		Если Не Данные.ФиксированныйОтбор.Свойство(Поле) Тогда
			Значения = ЗначенияКолонкиТаблицы(Данные.ИзмененияСоставаСтрок, Поле);
			
			Если Значения.Количество() = 1 Тогда
				Данные.ФиксированныйОтбор.Вставить(Поле, Значения[0]);
				Продолжить;
			КонецЕсли;
			
			Если Данные.ИзмеренияОтбора = Неопределено
			 Или Данные.ИзмеренияОтбора.Свойство(Поле) Тогда
				
				МассивИзмеренийОтбора.Добавить(Поле);
				ЗначенияИзмеренийОтбора.Вставить(Поле, Значения);
				
			ИначеЕсли Не ЗначениеЗаполнено(ИзмерениеБезФиксированногоОтбора) Тогда
				ИзмерениеБезФиксированногоОтбора.Вставить("Поле",     Поле);
				ИзмерениеБезФиксированногоОтбора.Вставить("Значения", Значения);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Если МассивИзмеренийОтбора.Количество() = 0 Тогда
		Если ЗначениеЗаполнено(ИзмерениеБезФиксированногоОтбора) Тогда
			Поле     = ИзмерениеБезФиксированногоОтбора.Поле;
			Значения = ИзмерениеБезФиксированногоОтбора.Значения;
		КонецЕсли;
		МассивИзмеренийОтбора.Добавить(Поле);
		ЗначенияИзмеренийОтбора.Вставить(Поле, Значения);
	КонецЕсли;
	
	Данные.Вставить("ИмяПервогоИзмерения", МассивИзмеренийОтбора[0]);
	Данные.Вставить("ЗначенияПервогоИзмерения", ЗначенияИзмеренийОтбора[Данные.ИмяПервогоИзмерения]);
	
	Если МассивИзмеренийОтбора.Количество() > 1 Тогда
		Данные.Вставить("ИмяВторогоИзмерения", МассивИзмеренийОтбора[1]);
		Данные.Вставить("ЗначенияВторогоИзмерения", ЗначенияИзмеренийОтбора[Данные.ИмяВторогоИзмерения]);
	Иначе
		Данные.Вставить("ИмяВторогоИзмерения", Неопределено);
		Данные.Вставить("ЗначенияВторогоИзмерения", Неопределено);
	КонецЕсли;
	
	Если МассивИзмеренийОтбора.Количество() > 2 Тогда
		Данные.Вставить("ИмяТретьегоИзмерения", МассивИзмеренийОтбора[2]);
		Данные.Вставить("ЗначенияТретьегоИзмерения", ЗначенияИзмеренийОтбора[Данные.ИмяТретьегоИзмерения]);
	Иначе
		Данные.Вставить("ИмяТретьегоИзмерения", Неопределено);
		Данные.Вставить("ЗначенияТретьегоИзмерения", Неопределено);
	КонецЕсли;
	
	Данные.Вставить("ПоляСравнения", ОписаниеКлючаЗаписи.СтрокаПолей);
	Данные.Вставить("НовыеЗаписиСодержатТолькоРазличия", Истина);
	Данные.Вставить("НовыеЗаписи", Данные.ИзмененияСоставаСтрок);
	Данные.Удалить("ИзмененияСоставаСтрок");
	Данные.Удалить("ИзмеренияОтбора");
	
	ОбновитьНаборыЗаписей(Данные, ЕстьИзменения);
	
КонецПроцедуры

// Возвращает пустую ссылку объекта метаданных ссылочного типа.
//
// Параметры:
//  ОписаниеОбъектаМетаданных - ОбъектМетаданных,
//                            - Тип - по которому можно найти объект метаданных,
//                            - Строка - полное имя объекта метаданных.
// Возвращаемое значение:
//  ЛюбаяСсылка
//
Функция ПустаяСсылкаОбъектаМетаданных(ОписаниеОбъектаМетаданных) Экспорт
	
	Если ТипЗнч(ОписаниеОбъектаМетаданных) = Тип("ОбъектМетаданных") Тогда
		ОбъектМетаданных = ОписаниеОбъектаМетаданных;
		
	ИначеЕсли ТипЗнч(ОписаниеОбъектаМетаданных) = Тип("Тип") Тогда
		ОбъектМетаданных = Метаданные.НайтиПоТипу(ОписаниеОбъектаМетаданных);
	Иначе
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ОписаниеОбъектаМетаданных);
	КонецЕсли;
	
	Если ОбъектМетаданных = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка в функции %1
			           |общего модуля %2.
			           |
			           |Неверный параметр %3.'"),
			"ПустаяСсылкаОбъектаМетаданных",
			"УправлениеДоступомСлужебный",
			"ОписаниеОбъектаМетаданных");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ПустаяСсылка = Неопределено;
	Попытка
		МенеджерОбъекта = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ОбъектМетаданных.ПолноеИмя());
		ПустаяСсылка = МенеджерОбъекта.ПустаяСсылка();
	Исключение
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка в функции %1
			           |общего модуля %2.
			           |
			           |Не удалось получить пустую ссылка для объекта метаданных
			           |""%3"".'"),
			"ПустаяСсылкаОбъектаМетаданных",
			"УправлениеДоступомСлужебный",
			ОбъектМетаданных.ПолноеИмя());
		ВызватьИсключение ТекстОшибки;
	КонецПопытки;
	
	Возврат ПустаяСсылка;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Прочие процедуры и функции

// Создает запрос выбора различий между строками регистра в заданной области данных
// (на основе отборов в параметре ПоляИОтбор).
//
// Параметры:
//  ТекстЗапросаВыбораНовых - Строка.
//
//  ПоляИОтбор - Массив из Структура - со свойствами "ИмяПоля", ИмяПараметраУсловияОтбора.
//
//  ПолноеИмяРегистра - Строка       - запрос старых формируется автоматически.
//                    - Неопределено - запрос старых берется из следующего параметра.
//
//  ТекстЗапросовВременныхТаблиц - Строка - запрос временных таблиц, если требуется.
//
//  ТекстЗапросаВыбораСтарых - Строка       - запрос старых, с учетом нестандартных отборов.
//                           - Неопределено - когда полное имя регистра определено.
//
// Возвращаемое значение:
//  Строка - текст запроса
//
Функция ТекстЗапросаВыбораИзменений(ТекстЗапросаВыбораНовых,
                                    ПоляИОтбор,
                                    ПолноеИмяРегистра            = Неопределено,
                                    ТекстЗапросовВременныхТаблиц = Неопределено,
                                    ТекстЗапросаВыбораСтарых     = Неопределено) Экспорт
	
	// Подготовка текста запроса старых данных.
	Если ПолноеИмяРегистра <> Неопределено Тогда
		ТекстЗапросаВыбораСтарых =
		"ВЫБРАТЬ
		|	&ВыбираемыеПоля,
		|	&ПодстановкаПоляВидИзмененияСтроки
		|ИЗ
		|	ПолноеИмяРегистра КАК СтарыеДанные
		|ГДЕ
		|	&УсловияОтбора";
	КонецЕсли;
	
	ВыбираемыеПоля = "";
	УсловияОтбора = "ИСТИНА";
	Для Каждого ОписаниеПоля Из ПоляИОтбор Цикл
		// Сборка выбираемых полей.
		ВыбираемыеПоля = ВыбираемыеПоля + СтрЗаменить(
			"
			|	СтарыеДанные.Поле,",
			"Поле",
			КлючИЗначение(ОписаниеПоля).Ключ);
			
		// Сборка условий отбора.
		Если ЗначениеЗаполнено(КлючИЗначение(ОписаниеПоля).Значение) Тогда
			УсловияОтбора = УсловияОтбора + СтрЗаменить(
				"
				|	И &ИмяПараметраУсловияОтбора", "&ИмяПараметраУсловияОтбора",  // @query-part-1
				КлючИЗначение(ОписаниеПоля).Значение);
		КонецЕсли;
	КонецЦикла;
	
	ТекстЗапросаВыбораСтарых =
		СтрЗаменить(ТекстЗапросаВыбораСтарых, "&ВыбираемыеПоля,",  ВыбираемыеПоля);
	
	ТекстЗапросаВыбораСтарых =
		СтрЗаменить(ТекстЗапросаВыбораСтарых, "&УсловияОтбора",    УсловияОтбора);
	
	ТекстЗапросаВыбораСтарых =
		СтрЗаменить(ТекстЗапросаВыбораСтарых, "ПолноеИмяРегистра", ПолноеИмяРегистра);
	
	Если СтрНайти(ТекстЗапросаВыбораСтарых, "&ПодстановкаПоляВидИзмененияСтроки") = 0 Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка в значении параметра %1
			           |процедуры %2 модуля %3.
			           |
			           |В тексте запроса отсутствует строка ""%4"".'"),
			"ТекстЗапросаВыбораСтарых",
			"ТекстЗапросаВыбораИзменений",
			"УправлениеДоступомСлужебный",
			"&ПодстановкаПоляВидИзмененияСтроки");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ТекстЗапросаВыбораСтарых = СтрЗаменить(
		ТекстЗапросаВыбораСтарых, "&ПодстановкаПоляВидИзмененияСтроки", "-1 КАК ВидИзмененияСтроки");
	
	Если СтрНайти(ТекстЗапросаВыбораНовых, "&ПодстановкаПоляВидИзмененияСтроки") = 0 Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка в значении параметра %1
			           |процедуры %2 модуля %3.
			           |
			           |В тексте запроса отсутствует строка ""%1"".'"),
			"ТекстЗапросаВыбораНовых",
			"ТекстЗапросаВыбораИзменений",
			"УправлениеДоступомСлужебный",
			"&ПодстановкаПоляВидИзмененияСтроки");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ТекстЗапросаВыбораНовых = СтрЗаменить(
		ТекстЗапросаВыбораНовых,  "&ПодстановкаПоляВидИзмененияСтроки", "1 КАК ВидИзмененияСтроки");
	
	// Подготовка текста запроса выбора изменений.
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	&ВыбираемыеПоля,
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки
	|ИЗ
	|	ТекстЗапросаВыбораНовыхИСтарыхСтрок КАК ВсеСтроки
	|	
	|СГРУППИРОВАТЬ ПО
	|	&ПоляГруппировки
	|	
	|ИМЕЮЩИЕ
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0";
	
	ТекстЗапросаВыбораНовыхИСтарыхСтрок =
	"	(" + ТекстЗапросаВыбораНовых + "
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	" + ТекстЗапросаВыбораСтарых + ")"; // @query-part-1
	
	ВыбираемыеПоля = "";
	ПоляГруппировки = "";
	Для Каждого ОписаниеПоля Из ПоляИОтбор Цикл
		// Сборка выбираемых полей.
		ВыбираемыеПоля = ВыбираемыеПоля + СтрЗаменить(
			"
			|	ВсеСтроки.Поле,",
			"Поле",
			КлючИЗначение(ОписаниеПоля).Ключ);
		
		// Сборка полей соединения.
		ПоляГруппировки = ПоляГруппировки + СтрЗаменить(
			"
			|	ВсеСтроки.Поле,",
			"Поле",
			КлючИЗначение(ОписаниеПоля).Ключ);
	КонецЦикла;
	ПоляГруппировки = Лев(ПоляГруппировки, СтрДлина(ПоляГруппировки)-1);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ВыбираемыеПоля,",  ВыбираемыеПоля);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляГруппировки", ПоляГруппировки);
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
		"ТекстЗапросаВыбораНовыхИСтарыхСтрок", ТекстЗапросаВыбораНовыхИСтарыхСтрок);
	
	Если ЗначениеЗаполнено(ТекстЗапросовВременныхТаблиц) Тогда
		ТекстЗапроса = ТекстЗапросовВременныхТаблиц +
		"
		|;
		|" + ТекстЗапроса;
	КонецЕсли;
	
	Возврат ТекстЗапроса;
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Обновление информационной базы.

// Заполняет обработчик разделенных данных, зависимый от изменения неразделенных данных.
//
// Параметры:
//   Параметры - Структура - структура параметров обработчиков:
//     * РазделенныеОбработчики - см. ОбновлениеИнформационнойБазы.НоваяТаблицаОбработчиковОбновления
// 
Процедура ЗаполнитьОбработчикиРазделенныхДанных(Параметры = Неопределено) Экспорт
	
	Если Параметры <> Неопределено И ЕстьИзмененияПараметровОграниченияДоступа() Тогда
		Обработчики = Параметры.РазделенныеОбработчики;
		Обработчик = Обработчики.Добавить();
		Обработчик.Версия = "*";
		Обработчик.РежимВыполнения = "Оперативно";
		Обработчик.Процедура = "УправлениеДоступомСлужебный.ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации";
	КонецЕсли;
	
КонецПроцедуры

// Обновляет вспомогательные данные, которые зависят от конфигурации частично.
//
// Обновляется при наличии изменений конфигурации, записанных в параметры
// ограничения доступа при обновлении базы данных на текущую версию конфигурации.
//
Процедура ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации(Параметры = Неопределено) Экспорт
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ПраваРолей".
	РегистрыСведений.ТаблицыГруппДоступа.ОбновитьДанныеРегистраПоИзменениямКонфигурации();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.СвойстваВидовДоступа".
	ОбновитьГруппыИНаборыЗначенийДоступаПриИзмененииТиповГруппИЗначений();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ВозможныеПраваДляНастройкиПравОбъектов".
	РегистрыСведений.НастройкиПравОбъектов.ОбновитьВспомогательныеДанныеРегистраПоИзменениямКонфигурации();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ОписаниеПоставляемыхПрофилей".
	Справочники.ПрофилиГруппДоступа.ОбновитьПоставляемыеПрофилиПоИзменениямКонфигурации();
	Справочники.ПрофилиГруппДоступа.ОбновитьНепоставляемыеПрофилиПоИзменениямКонфигурации();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ПредопределенныеПрофилиГруппДоступа".
	Справочники.ГруппыДоступа.ПометитьНаУдалениеГруппыДоступаПомеченныхПрофилей();
	
	// Параметр "СтандартныеПодсистемы.УправлениеДоступом.ВерсияТекстовОграниченияДоступа".
	РегистрыСведений.ПараметрыОграниченияДоступа.ЗапланироватьОбновлениеДоступаПоИзменениямКонфигурации();
	
КонецПроцедуры

// Обновляет настройки и включает регламентное задание.
Процедура ВключитьЗаполнениеДанныхДляОграниченияДоступа() Экспорт
	
	Использование = Константы.ОграничиватьДоступНаУровнеЗаписей.Получить();
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		УстановитьЗаполнениеДанныхДляОграниченияДоступа(Использование);
	Иначе
		Расписание = Новый РасписаниеРегламентногоЗадания;
		Расписание.ПериодНедель = 1;
		Расписание.ПериодПовтораДней = 1;
		Расписание.ПериодПовтораВТечениеДня = 300;
		Расписание.ПаузаПовтора = 90;
		
		ЗаданиеМетаданные = Метаданные.РегламентныеЗадания.ЗаполнениеДанныхДляОграниченияДоступа;
		Задание = РегламентныеЗаданияСервер.ПолучитьРегламентноеЗадание(ЗаданиеМетаданные);
		
		Задание.Использование = Использование;
		Задание.Расписание = Расписание;
		
		Задание.ИнтервалПовтораПриАварийномЗавершении
			= ЗаданиеМетаданные.ИнтервалПовтораПриАварийномЗавершении;
		
		Задание.КоличествоПовторовПриАварийномЗавершении
			= ЗаданиеМетаданные.КоличествоПовторовПриАварийномЗавершении;
		
		Задание.Записать();
	КонецЕсли;
	
КонецПроцедуры

// Обновляет данные профиля "ИнтерактивноеОткрытиеВнешнихОтчетовИОбработок".
Процедура ОбновитьДанныеПрофиляОткрытиеВнешнихОтчетовИОбработок() Экспорт
	
	УникальныйИдентификаторПрофиля = Новый УникальныйИдентификатор(
		ИдентификаторПрофиляОткрытиеВнешнихОтчетовИОбработок());
	
	Ссылка = Справочники.ПрофилиГруппДоступа.ПолучитьСсылку(УникальныйИдентификаторПрофиля);
	Если ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Ссылка") = Неопределено Тогда
		Возврат;
	КонецЕсли;
	СсылкаПометитьНаУдаление = Ложь;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
	ЭлементБлокировки.УстановитьЗначение("Ссылка", Ссылка);
	
	Если ОбщегоНазначения.РазделениеВключено() Тогда
		СсылкаПометитьНаУдаление = Истина;
	Иначе
		ОписаниеПрофиля = ОписаниеПрофиляОткрытиеВнешнихОтчетовИОбработок();
		ПоставляемыйПрофильСсылка = Справочники.ПрофилиГруппДоступа.ПоставляемыйПрофильПоИдентификатору(
			ОписаниеПрофиля.Имя);
		
		Если Ссылка <> ПоставляемыйПрофильСсылка Тогда
			Если ПоставляемыйПрофильСсылка <> Неопределено Тогда
				СсылкаПометитьНаУдаление = Истина;
			Иначе
				// Установка идентификатора поставляемых данных.
				НачатьТранзакцию();
				Попытка
					Блокировка.Заблокировать();
					ПрофильОбъект = Ссылка.ПолучитьОбъект();
					ПрофильОбъект.ИдентификаторПоставляемыхДанных = УникальныйИдентификаторПрофиля;
					ПрофильОбъект.Комментарий = "";
					ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ПрофильОбъект, Ложь);
					ЗафиксироватьТранзакцию();
				Исключение
					ОтменитьТранзакцию();
					ВызватьИсключение;
				КонецПопытки;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если СсылкаПометитьНаУдаление Тогда
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ПрофильОбъект = Ссылка.ПолучитьОбъект();
			ПрофильОбъект.ПометкаУдаления = Истина;
			ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ПрофильОбъект, Ложь);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		ГруппыПрофиля = ГруппыПрофиля(Ссылка, Ложь);
		Для Каждого СсылкаГруппы Из ГруппыПрофиля Цикл
			Блокировка = Новый БлокировкаДанных;
			ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
			ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаГруппы);
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				ГруппаОбъект = СсылкаГруппы.ПолучитьОбъект();
				ГруппаОбъект.ПометкаУдаления = Истина;
				ОбновлениеИнформационнойБазы.ЗаписатьОбъект(ГруппаОбъект, Ложь);
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Только для внутреннего использования.
Процедура ОбновитьВспомогательныеДанныеГруппДоступа(Параметры) Экспорт
	
	Справочники.ГруппыДоступа.ОбновитьВспомогательныеДанныеГруппДоступа(Параметры);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Вспомогательные процедуры и функции.

// Возвращаемое значение:
//   см. РегистрыСведений.ПраваРолей.ТаблицаПравРолей
//
Функция ПраваРолейРасширений() Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ПраваРолейРасширений = Неопределено;
	
	УстановитьЗаписьПараметровОграниченияДоступаВТекущемСеансе(Истина);
	Попытка
		ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений(ПраваРолейРасширений);
	Исключение
		УстановитьЗаписьПараметровОграниченияДоступаВТекущемСеансе(Ложь);
		ВызватьИсключение;
	КонецПопытки;
	УстановитьЗаписьПараметровОграниченияДоступаВТекущемСеансе(Ложь);
	
	Возврат ПраваРолейРасширений;
	
КонецФункции

// Параметры:
//  ПраваРолейРасширений - см. РегистрыСведений.ПраваРолей.ТаблицаПравРолей
//
Процедура ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений(ПраваРолейРасширений = Неопределено)
	
	ПустыеПраваРолейРасширений = РегистрыСведений.ПраваРолей.ТаблицаПравРолей(Истина, Истина, Истина);
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.ОбменДанными") Тогда
		МодульОбменДаннымиСервер = ОбщегоНазначения.ОбщийМодуль("ОбменДаннымиСервер");
		Если МодульОбменДаннымиСервер.НастройкаПодчиненногоУзлаРИБ() Тогда
			ПраваРолейРасширений = ПустыеПраваРолейРасширений;
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	// Заполнение прав ролей расширений, которые состоят
	// из изменений прав на объекты конфигурации и прав на объекты расширений.
	УстановитьНовыеПраваРолейРасширений = Ложь;
	Если ЗначениеЗаполнено(ПараметрыСеанса.ПодключенныеРасширения) Тогда
		ХранилищеПравРолейРасширений = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(
			"СтандартныеПодсистемы.УправлениеДоступом.ПраваРолей"); // ХранилищеЗначения
		
		Если ХранилищеПравРолейРасширений = Неопределено Тогда
			ПраваРолейРасширений = Неопределено;
		Иначе
			ПраваРолейРасширений = ЗначениеИзХранилища(ХранилищеПравРолейРасширений);
			Если Не ЭтоПраваРолейРасширений(ПраваРолейРасширений, ПустыеПраваРолейРасширений) Тогда
				ПраваРолейРасширений = Неопределено;
			КонецЕсли;
		КонецЕсли;
		Если ПраваРолейРасширений = Неопределено Тогда
			Запрос = РегистрыСведений.ПраваРолей.ЗапросИзменений(Истина);
			ПраваРолейРасширений = Запрос.Выполнить().Выгрузить();
			УстановитьНовыеПраваРолейРасширений = Истина;
		КонецЕсли;
	Иначе
		ПраваРолейРасширений = ПустыеПраваРолейРасширений;
	КонецЕсли;
	
	// Проверка необходимости обновления регистра ТаблицыГруппДоступа.
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ПараметрыОбновленияТаблицГруппДоступа";
	ПараметрыОбновления = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
	
	Если ТипЗнч(ПараметрыОбновления) <> Тип("Структура")
	 Или Не ПараметрыОбновления.Свойство("ПоследниеПраваРолейРасширений")
	 Или Не ЭтоПраваРолейРасширений(ПараметрыОбновления.ПоследниеПраваРолейРасширений, ПустыеПраваРолейРасширений) Тогда
		
		ТребуетсяОбновление = Истина;
		ПоследниеПраваРолейРасширений = Неопределено;
	Иначе
		ПоследниеПраваРолейРасширений = ПараметрыОбновления.ПоследниеПраваРолейРасширений;
		ТребуетсяОбновление = ПраваРолейРасширенийИзменились(ПраваРолейРасширений, ПоследниеПраваРолейРасширений);
	КонецЕсли;
	
	Если Не ТребуетсяОбновление Тогда
		Если УстановитьНовыеПраваРолейРасширений Тогда;
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(
				"СтандартныеПодсистемы.УправлениеДоступом.ПраваРолей",
				Новый ХранилищеЗначения(ПраваРолейРасширений));
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если ПоследниеПраваРолейРасширений = Неопределено Тогда
		// Так как последние права ролей расширений недоступны, требуется обновить весь регистр.
		ОбъектыСИзменениемПравДляОбновления = Неопределено;
	Иначе
		ТекущиеОбъектыСИзменениемПрав = РегистрыСведений.ПраваРолей.ИзмененныеОбъектыМетаданных(
			ПраваРолейРасширений.Скопировать());
		
		ПоследниеОбъектыСИзменениемПрав = РегистрыСведений.ПраваРолей.ИзмененныеОбъектыМетаданных(
			ПоследниеПраваРолейРасширений.Скопировать());
		
		ОбъектыСИзменениемПравДляОбновления = Новый Массив(ТекущиеОбъектыСИзменениемПрав);
		Для Каждого Идентификатор Из ПоследниеОбъектыСИзменениемПрав Цикл
			ОбъектыСИзменениемПравДляОбновления.Добавить(Идентификатор);
		КонецЦикла;
	КонецЕсли;
	
	НовыеПараметрыОбновления = Новый Структура;
	НовыеПараметрыОбновления.Вставить("ПоследниеПраваРолейРасширений", ПраваРолейРасширений);
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("РегистрСведений.ТаблицыГруппДоступа");
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
	ЭлементБлокировки.УстановитьЗначение("ВерсияРасширений", Справочники.ВерсииРасширений.ПустаяСсылка());
	ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		УжеИзменен = Ложь;
		СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
		Если УжеИзменен Тогда
			ПроверитьАктуальностьМетаданных();
		КонецЕсли;
		Если УстановитьНовыеПраваРолейРасширений Тогда;
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(
				"СтандартныеПодсистемы.УправлениеДоступом.ПраваРолей",
				Новый ХранилищеЗначения(ПраваРолейРасширений));
		КонецЕсли;
		СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра,
			НовыеПараметрыОбновления, Истина);
		РегистрыСведений.ТаблицыГруппДоступа.ОбновитьДанныеРегистра(, ОбъектыСИзменениемПравДляОбновления);
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений.
// 
// Параметры:
//  ПраваРолейРасширений       - см. РегистрыСведений.ПраваРолей.ТаблицаПравРолей
//  ПустыеПраваРолейРасширений - см. РегистрыСведений.ПраваРолей.ТаблицаПравРолей
//
Функция ЭтоПраваРолейРасширений(ПраваРолейРасширений, ПустыеПраваРолейРасширений)
	
	Если ТипЗнч(ПраваРолейРасширений) <> Тип("ТаблицаЗначений") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ПраваРолейРасширений.Колонки.Количество() <> ПустыеПраваРолейРасширений.Колонки.Количество() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Для Каждого Колонка Из ПустыеПраваРолейРасширений.Колонки Цикл
		НайденнаяКолонка = ПраваРолейРасширений.Колонки.Найти(Колонка.Имя);
		Если НайденнаяКолонка = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
		Если Колонка.ТипЗначения <> НайденнаяКолонка.ТипЗначения Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
	
КонецФункции

// Для процедуры ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений.
// 
// Параметры:
//  НовыеПраваРолейРасширений  - см. РегистрыСведений.ПраваРолей.ТаблицаПравРолей
//  СтарыеПраваРолейРасширений - см. РегистрыСведений.ПраваРолей.ТаблицаПравРолей
//
Функция ПраваРолейРасширенийИзменились(НовыеПраваРолейРасширений, СтарыеПраваРолейРасширений)
	
	Если НовыеПраваРолейРасширений.Количество() <> СтарыеПраваРолейРасширений.Количество() Тогда
		Возврат Истина;
	КонецЕсли;
	
	Отбор = Новый Структура;
	Поля = Новый Массив;
	Для Каждого Колонка Из НовыеПраваРолейРасширений.Колонки Цикл
		Поля.Добавить(Колонка.Имя);
		Отбор.Вставить(Колонка.Имя);
	КонецЦикла;
	СтарыеПраваРолейРасширений.Индексы.Добавить(СтрСоединить(Поля, ","));
	
	Для Каждого Строка Из НовыеПраваРолейРасширений Цикл
		ЗаполнитьЗначенияСвойств(Отбор, Строка);
		Если СтарыеПраваРолейРасширений.НайтиСтроки(Отбор).Количество() <> 1 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

Процедура ПриИзмененииНаборовЗначенийДоступа(Знач СсылкаНаОбъект, ОбновлениеИБ = Ложь)
	
	СсылкиНаЗависимыеОбъекты = Новый Массив;
	
	УправлениеДоступомПереопределяемый.ПриИзмененииНаборовЗначенийДоступа(
		СсылкаНаОбъект, СсылкиНаЗависимыеОбъекты);
	
	Для каждого СсылкаНаЗависимыйОбъект Из СсылкиНаЗависимыеОбъекты Цикл
		
		Если СсылкаНаЗависимыйОбъект.Метаданные().ТабличныеЧасти.Найти("НаборыЗначенийДоступа") = Неопределено Тогда
			// Изменение объекта не требуется.
			// @skip-check query-in-loop - Порционная обработка данных
			ЗаписатьНаборыЗначенийДоступа(СсылкаНаЗависимыйОбъект, , ОбновлениеИБ);
		Иначе
			// Изменение объекта требуется.
			Объект = СсылкаНаЗависимыйОбъект.ПолучитьОбъект();
			Таблица = ПолучитьНаборыЗначенийДоступаТабличнойЧасти(Объект);
			Если НЕ НаборыЗначенийДоступаТабличнойЧастиИзменены(СсылкаНаЗависимыйОбъект, Таблица) Тогда
				Продолжить;
			КонецЕсли;
			ПодготовитьНаборыЗначенийДоступаКЗаписи(Неопределено, Таблица, Ложь);
			Попытка
				ЗаблокироватьДанныеДляРедактирования(СсылкаНаЗависимыйОбъект, Объект.ВерсияДанных);
				Объект.ДополнительныеСвойства.Вставить("ЗаписатьНаборыЗначенийДоступа");
				Объект.ДополнительныеСвойства.Вставить("ЗаписатьЗависимыеНаборыЗначенийДоступа");
				Объект.ДополнительныеСвойства.Вставить("НаборыЗначенийДоступаТабличнойЧастиЗаполнены");
				Объект.НаборыЗначенийДоступа.Загрузить(Таблица);
				Если ОбновлениеИБ Тогда
					Объект.ДополнительныеСвойства.Вставить("ЗаписьНаборовЗначенийДоступаПриОбновленииИБ");
					ОбновлениеИнформационнойБазы.ЗаписатьДанные(Объект);
				Иначе
					Объект.ОбменДанными.Загрузка = Истина;
					// АПК:1327-выкл - №783.1.4.1 Допустимо оставить запись без
					// предварительной управляемой блокировки объекта, так как только
					// для RLS в формате БСП 2.х (устарело) и ни разу не вызывало проблем.
					Объект.Записать();
					// АПК:1327-вкл.
				КонецЕсли;
				РазблокироватьДанныеДляРедактирования(СсылкаНаЗависимыйОбъект);
			Исключение
				ИнформацияОбОшибке = ИнформацияОбОшибке();
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось обновить зависимый набор значений доступа объекта
					           |""%1"" по причине:
					           |
					           |%2'"),
					Строка(СсылкаНаЗависимыйОбъект),
					ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
				ВызватьИсключение ТекстОшибки;
			КонецПопытки;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Функция ИдентификаторПрофиляОткрытиеВнешнихОтчетовИОбработок()
	
	Возврат "1b3472f6-4d87-11e6-8264-5404a6a6895d";
	
КонецФункции

Функция ЭтоПрофильОткрытиеВнешнихОтчетовИОбработок(Профиль) Экспорт
	
	Если ТипЗнч(Профиль) = Тип("СправочникСсылка.ПрофилиГруппДоступа") Тогда
		ИдентификаторПрофиля = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Профиль, "ИдентификаторПоставляемыхДанных");
		
	ИначеЕсли Не Профиль.ЭтоГруппа Тогда
		ИдентификаторПрофиля = Профиль.ИдентификаторПоставляемыхДанных;
	Иначе
		Возврат Ложь;
	КонецЕсли;
	
	Возврат ВРег(Строка(ИдентификаторПрофиля)) = ВРег(ИдентификаторПрофиляОткрытиеВнешнихОтчетовИОбработок());
	
КонецФункции

Функция ГруппыДоступаПрофиляОткрытиеВнешнихОтчетовИОбработок()
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИдентификаторПоставляемыхДанных",
		Новый УникальныйИдентификатор(ИдентификаторПрофиляОткрытиеВнешнихОтчетовИОбработок()));
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ГруппыДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ГруппыДоступа КАК ГруппыДоступа
	|ГДЕ
	|	ГруппыДоступа.Профиль.ИдентификаторПоставляемыхДанных = &ИдентификаторПоставляемыхДанных";
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

Функция ОписаниеПрофиляОткрытиеВнешнихОтчетовИОбработок() Экспорт
	
	ОписаниеПрофиля = УправлениеДоступом.НовоеОписаниеПрофиляГруппДоступа();
	ОписаниеПрофиля.Имя           = "ОткрытиеВнешнихОтчетовИОбработок";
	ОписаниеПрофиля.Родитель      = "ДополнительныеПрофили";
	ОписаниеПрофиля.Идентификатор = ИдентификаторПрофиляОткрытиеВнешнихОтчетовИОбработок();
	
	ОписаниеПрофиля.Наименование =
		НСтр("ru = 'Открытие внешних отчетов и обработок'", ОбщегоНазначения.КодОсновногоЯзыка());
	
	ОписаниеПрофиля.Описание =
		НСтр("ru = 'Предоставляет право открытия внешних отчетов и обработок из меню ""Файл - Открыть"".
		           |Состав ролей профиля не рекомендуется изменять.'");
	
	ОписаниеПрофиля.Роли.Добавить("ИнтерактивноеОткрытиеВнешнихОтчетовИОбработок");
	
	Возврат ОписаниеПрофиля;
	
КонецФункции

Функция ГруппаДоступаОткрытиеВнешнихОтчетовИОбработок(СвойстваПрофиля)
	
	// Поиск по идентификатору.
	УникальныйИдентификатор = Новый УникальныйИдентификатор("f6929bcb-532f-11e6-a20f-5404a6a6895d");
	Ссылка = Справочники.ГруппыДоступа.ПолучитьСсылку(УникальныйИдентификатор);
	СсылкаСуществует = (ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Ссылка") <> Неопределено);
	Если СсылкаСуществует Тогда
		Возврат Ссылка;
	КонецЕсли;
	
	// Поиск по профилю.
	ГруппыПрофиля = ГруппыПрофиля(СвойстваПрофиля.Ссылка, Неопределено);
	Если ГруппыПрофиля.Количество() > 0 Тогда
		Возврат ГруппыПрофиля[0];
	КонецЕсли;
	
	// Создание группы.
	ГруппаДоступаОбъект = Справочники.ГруппыДоступа.СоздатьЭлемент();
	ГруппаДоступаОбъект.УстановитьСсылкуНового(Ссылка);
	ГруппаДоступаОбъект.Наименование = СвойстваПрофиля.Наименование;
	ГруппаДоступаОбъект.Профиль      = СвойстваПрофиля.Ссылка;
	ГруппаДоступаОбъект.Комментарий  =
		НСтр("ru = 'Предоставляет право открытия внешних отчетов и обработок из меню ""Файл - Открыть"".'",
			ОбщегоНазначения.КодОсновногоЯзыка());
	
	ГруппаДоступаОбъект.Записать(); // Важно, чтобы созданная группа "уехала" в подчиненный узел.
	
	Возврат ГруппаДоступаОбъект.Ссылка;
	
КонецФункции

Функция ГруппыПрофиля(СсылкаПрофиля, ПометкаУдаления)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ГруппыДоступа.Ссылка
	|ИЗ
	|	Справочник.ГруппыДоступа КАК ГруппыДоступа
	|ГДЕ
	|	ГруппыДоступа.Профиль = &Профиль
	|	И ГруппыДоступа.ПометкаУдаления = &ПометкаУдаления
	|
	|УПОРЯДОЧИТЬ ПО
	|	ГруппыДоступа.ПометкаУдаления";
	
	Запрос.УстановитьПараметр("Профиль", СсылкаПрофиля);
	
	Если ПометкаУдаления = Неопределено Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "И ГруппыДоступа.ПометкаУдаления = &ПометкаУдаления", ""); // @query-part-1
	Иначе
		Запрос.УстановитьПараметр("ПометкаУдаления", ПометкаУдаления);
	КонецЕсли;
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Для функция РазрешенныеЗначенияДляДинамическогоСписка, ЕстьОграничениеПоВидуДоступа.
Функция ТекстЗапросаГруппДоступа()
	
	Возврат
	"ВЫБРАТЬ
	|	ГруппыДоступа.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ ГруппыДоступаПользователя
	|ИЗ
	|	Справочник.ИдентификаторыОбъектовМетаданных КАК СвойстваТекущейТаблицы
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа КАК ГруппыДоступа
	|		ПО (СвойстваТекущейТаблицы.ПолноеИмя = &ИмяОсновнойТаблицыСписка)
	|			И (ИСТИНА В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ИСТИНА
	|				ИЗ
	|					РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа
	|				ГДЕ
	|					ТаблицыГруппДоступа.Таблица = СвойстваТекущейТаблицы.Ссылка
	|					И ТаблицыГруппДоступа.ГруппаДоступа = ГруппыДоступа.Ссылка))
	|			И (ГруппыДоступа.Ссылка В
	|				(ВЫБРАТЬ
	|					ГруппыДоступаПользователи.Ссылка КАК ГруппаДоступа
	|				ИЗ
	|					Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|						ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|						ПО
	|							СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь
	|								И СоставыГруппПользователей.ГруппаПользователей = ГруппыДоступаПользователи.Пользователь))";
	
КонецФункции

// Для процедур ОбновитьНаборЗаписей, ОбновитьНаборыЗаписей, ОбновитьРегистрСведений.
Процедура ЗаполнитьПараметры(ВходныеПараметры, Знач ВсеПараметры, Знач ОбязательныеПараметры = "")
	
	Если ТипЗнч(ВходныеПараметры) = Тип("Структура") Тогда
		Параметры = ВходныеПараметры;
	ИначеЕсли ВходныеПараметры = Неопределено Тогда
		Параметры = Новый Структура;
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректный тип набора свойств ""%1"".
			           |Допустимые типы: %2, %3.'"),
			ТипЗнч(ВходныеПараметры), "Структура", "Неопределено");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Для каждого КлючИЗначение Из Параметры Цикл
		Если Не ВсеПараметры.Свойство(КлючИЗначение.Ключ) Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Указан несуществующий параметр %1'"),
				КлючИЗначение.Ключ);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ВсеПараметры[КлючИЗначение.Ключ] = Параметры[КлючИЗначение.Ключ];
	КонецЦикла;
	
	Если ЗначениеЗаполнено(ОбязательныеПараметры) Тогда
		ОбязательныеПараметры = Новый Структура(ОбязательныеПараметры);
		
		Для каждого КлючИЗначение Из ОбязательныеПараметры Цикл
			Если Не Параметры.Свойство(КлючИЗначение.Ключ) Тогда
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не указан обязательный параметр %1'"),
					КлючИЗначение.Ключ);
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ВходныеПараметры = ВсеПараметры;
	
КонецПроцедуры

// Для процедур ПриОтправкеДанныхГлавному, ПриОтправкеДанныхПодчиненному,
// ПриПолученииДанныхОтГлавного, ПриПолученииДанныхОтПодчиненного.
//
Функция ОбъектПодсистемыУправлениеДоступомТолькоДляСозданияНачальногоОбраза(ЭлементДанных)
	
	Возврат ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ПраваРолей")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ЗависимостиПравДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ТаблицыГруппДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ЗначенияГруппДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ЗначенияГруппДоступаПоУмолчанию")
	    Или ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.КлючиДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.НаборыГруппДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.КлючиДоступаВнешнихПользователей")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.КлючиДоступаГруппДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.КлючиДоступаНаборовГруппДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.КлючиДоступаКОбъектам")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.КлючиДоступаКРегистрам")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.КлючиДоступаПользователей")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ОбновлениеКлючейДоступаКДанным")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ОбновлениеКлючейДоступаПользователей")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ПараметрыОграниченияДоступа")
	    Или ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ИспользуемыеВидыДоступаПоТаблицам")
	    Или ТипЗнч(ЭлементДанных) = Тип("КонстантаМенеджерЗначения.ПервоеОбновлениеДоступаЗавершилось");
	
КонецФункции

// Для процедур ПриПолученииДанныхОтГлавного, ПриПолученииДанныхОтПодчиненного
Процедура ПриПолученииДанныхОтГлавногоИлиОтПодчиненного(ЭлементДанных)
	
	Если ТипЗнч(ЭлементДанных) = Тип("КонстантаМенеджерЗначения.ОграничиватьДоступНаУровнеЗаписей") Тогда
		Константы.ОграничиватьДоступНаУровнеЗаписей.СоздатьМенеджерЗначения().ЗарегистрироватьИзменениеПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	Если ТипЗнч(ЭлементДанных) = Тип("РегистрСведенийНаборЗаписей.ИспользуемыеВидыДоступа") Тогда
		РегистрыСведений.ИспользуемыеВидыДоступа.ЗарегистрироватьИзменениеПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	Если ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ПрофилиГруппДоступа") Тогда
		// Роли расширений назначаются независимо во всех РИБ-узлах.
		Справочники.ПрофилиГруппДоступа.ВосстановитьСоставРолейРасширений(ЭлементДанных);
		// Регистрация измененного профиля для обновления вспомогательных данных.
		Справочники.ПрофилиГруппДоступа.ЗарегистрироватьПрофильИзмененныйПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	Если ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ГруппыДоступа") Тогда
		// Администраторы назначаются независимо во всех РИБ-узлах.
		Справочники.ГруппыДоступа.ВосстановитьСоставУчастниковГруппыДоступаАдминистраторы(ЭлементДанных);
		// Регистрация измененной группы доступа для обновления вспомогательных данных.
		Справочники.ГруппыДоступа.ЗарегистрироватьГруппуДоступаИзмененнуюПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	// Регистрация пользователей, измененных в группе пользователей, для обновления ролей после получения данных.
	Если ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ГруппыПользователей")
	 Или ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ГруппыВнешнихПользователей") Тогда
		Справочники.ГруппыДоступа.ЗарегистрироватьПользователейГруппыПользователейИзмененнойПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	// Регистрация пользователя, измененного при загрузке для обновления ролей после загрузки.
	Если ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.Пользователи")
	 Или ТипЗнч(ЭлементДанных) = Тип("СправочникОбъект.ВнешниеПользователи") Тогда
		Справочники.ГруппыДоступа.ЗарегистрироватьПользователяИзмененногоПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	Если ТипЗнч(ЭлементДанных) <> Тип("УдалениеОбъекта") Тогда
		Возврат;
	КонецЕсли;
	
	ЭлементДанных = ЭлементДанных; // УдалениеОбъекта
	
	// Регистрация измененного профиля для обновления вспомогательных данных после загрузки.
	Если ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.ПрофилиГруппДоступа") Тогда
		Справочники.ПрофилиГруппДоступа.ЗарегистрироватьПрофильИзмененныйПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	// Регистрация измененной группы доступа для обновления вспомогательных данных после загрузки.
	Если ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.ГруппыДоступа") Тогда
		Справочники.ГруппыДоступа.ЗарегистрироватьГруппуДоступаИзмененнуюПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	// Регистрация пользователей, измененных в группе пользователей, для обновления ролей после загрузки.
	Если ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.ГруппыПользователей")
	 Или ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.ГруппыВнешнихПользователей") Тогда
		Справочники.ГруппыДоступа.ЗарегистрироватьПользователейГруппыПользователейИзмененнойПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
	// Регистрация пользователя, измененного при загрузке для обновления ролей после загрузки.
	Если ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.Пользователи")
	 Или ТипЗнч(ЭлементДанных.Ссылка) = Тип("СправочникСсылка.ВнешниеПользователи") Тогда
		Справочники.ГруппыДоступа.ЗарегистрироватьПользователяИзмененногоПриЗагрузке(ЭлементДанных);
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ПослеПолученияДанных, ПослеОбновленияИнформационнойБазы.
Процедура ОбновитьВспомогательныеДанныеЭлементовИзмененныхПриЗагрузке()
	
	РегистрыСведений.ПараметрыРаботыВерсийРасширений.ЗаблокироватьДляИзмененияВФайловойИБ();
	
	Константы.ОграничиватьДоступНаУровнеЗаписей.СоздатьМенеджерЗначения().ОбработатьИзменениеЗарегистрированноеПриЗагрузке();
	РегистрыСведений.ИспользуемыеВидыДоступа.ОбработатьИзменениеЗарегистрированноеПриЗагрузке();
	Справочники.ПрофилиГруппДоступа.ОбновитьВспомогательныеДанныеПрофилейИзмененныхПриЗагрузке();
	Справочники.ГруппыДоступа.ОбновитьВспомогательныеДанныеГруппДоступаИзмененныхПриЗагрузке();
	Справочники.ГруппыДоступа.ОбновитьРолиПользователейИзмененныхПриЗагрузке();
	
КонецПроцедуры

// Для процедур ВключитьПользователяВГруппуДоступа, ИсключитьПользователяИзГруппыДоступа
// и функции НайтиПользователяВГруппеДоступа.

Функция ОбработатьСвязьПользователяСГруппойДоступа(Пользователь, ПоставляемыйПрофиль, Включить = Неопределено)
	
	Если ТипЗнч(Пользователь) <> Тип("СправочникСсылка.Пользователи")
	   И ТипЗнч(Пользователь) <> Тип("СправочникСсылка.ГруппыПользователей")
	   И ТипЗнч(Пользователь) <> Тип("СправочникСсылка.ВнешниеПользователи")
	   И ТипЗнч(Пользователь) <> Тип("СправочникСсылка.ГруппыВнешнихПользователей") Тогда
		
		Возврат Ложь;
	КонецЕсли;
	
	ИдентификаторПоставляемогоПрофиля = Неопределено;
	
	Если ТипЗнч(ПоставляемыйПрофиль) = Тип("Строка") Тогда
		Если СтроковыеФункцииКлиентСервер.ЭтоУникальныйИдентификатор(ПоставляемыйПрофиль) Тогда
			
			ИдентификаторПоставляемогоПрофиля = ПоставляемыйПрофиль;
			
			ПоставляемыйПрофиль = Справочники.ПрофилиГруппДоступа.ПоставляемыйПрофильПоИдентификатору(
				ИдентификаторПоставляемогоПрофиля,, Истина);
		Иначе
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Если ТипЗнч(ПоставляемыйПрофиль) <> Тип("СправочникСсылка.ПрофилиГруппДоступа") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ИдентификаторПоставляемогоПрофиля = Неопределено Тогда
		ИдентификаторПоставляемогоПрофиля =
			Справочники.ПрофилиГруппДоступа.ИдентификаторПоставляемогоПрофиля(ПоставляемыйПрофиль);
	КонецЕсли;
	
	Если ИдентификаторПоставляемогоПрофиля = Справочники.ПрофилиГруппДоступа.ИдентификаторПрофиляАдминистратор() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	СвойстваПрофиля = Справочники.ПрофилиГруппДоступа.СвойстваПоставляемогоПрофиля(ИдентификаторПоставляемогоПрофиля); 
	
	Если СвойстваПрофиля = Неопределено
	 ИЛИ СвойстваПрофиля.ВидыДоступа.Количество() <> 0 Тогда
		
		Возврат Ложь;
	КонецЕсли;
	
	ГруппаДоступа = Неопределено;
	
	Если УпрощенныйИнтерфейсНастройкиПравДоступа() Тогда
		
		Если ТипЗнч(Пользователь) <> Тип("СправочникСсылка.Пользователи")
		   И ТипЗнч(Пользователь) <> Тип("СправочникСсылка.ВнешниеПользователи") Тогда
			
			Возврат Ложь;
		КонецЕсли;
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("Профиль", ПоставляемыйПрофиль);
		Запрос.УстановитьПараметр("Пользователь", Пользователь);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ГруппыДоступа.Ссылка КАК Ссылка
		|ИЗ
		|	Справочник.ГруппыДоступа КАК ГруппыДоступа
		|ГДЕ
		|	ГруппыДоступа.Профиль = &Профиль
		|	И ГруппыДоступа.Пользователь = &Пользователь";
		Выборка = Запрос.Выполнить().Выбрать();
		Если Выборка.Следующий() Тогда
			ГруппаДоступа = Выборка.Ссылка;
		КонецЕсли;
		
		Если ГруппаДоступа = Неопределено Тогда
			Если Включить <> Истина Тогда
				Возврат Ложь;
			Иначе
				ГруппаДоступа = Справочники.ГруппыДоступа.СоздатьЭлемент();
				ГруппаДоступа.Наименование = СвойстваПрофиля.Наименование;
				ГруппаДоступа.Профиль      = ПоставляемыйПрофиль;
				ГруппаДоступа.Пользователь = Пользователь;
				ГруппаДоступа.Пользователи.Добавить().Пользователь = Пользователь;
				ГруппаДоступа.Записать();
				Возврат Истина;
			КонецЕсли;
		КонецЕсли;
	Иначе
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ПоставляемыйПрофиль", ПоставляемыйПрофиль);
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ГруппыДоступа.Ссылка КАК Ссылка,
		|	ГруппыДоступа.ОсновнаяГруппаДоступаПоставляемогоПрофиля
		|ИЗ
		|	Справочник.ГруппыДоступа КАК ГруппыДоступа
		|ГДЕ
		|	ГруппыДоступа.Профиль = &ПоставляемыйПрофиль
		|
		|УПОРЯДОЧИТЬ ПО
		|	ГруппыДоступа.ОсновнаяГруппаДоступаПоставляемогоПрофиля УБЫВ";
		Выборка = Запрос.Выполнить().Выбрать();
		Если Выборка.Следующий() Тогда
			ГруппаДоступа = Выборка.Ссылка; // СправочникСсылка.ГруппыДоступа - 
		КонецЕсли;
		
		Если ГруппаДоступа = Неопределено Тогда
			Если Включить <> Истина Тогда
				Возврат Ложь;
			Иначе
				ГруппаДоступа = Справочники.ГруппыДоступа.СоздатьЭлемент();
				ГруппаДоступа.ОсновнаяГруппаДоступаПоставляемогоПрофиля = Истина;
				ГруппаДоступа.Наименование = СвойстваПрофиля.Наименование;
				ГруппаДоступа.Профиль = ПоставляемыйПрофиль;
				ГруппаДоступа.Пользователи.Добавить().Пользователь = Пользователь;
				ГруппаДоступа.Записать();
				Возврат Истина;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Ссылка", ГруппаДоступа);
	Запрос.УстановитьПараметр("Пользователь", Пользователь);
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	Справочник.ГруппыДоступа.Пользователи КАК УчастникиГруппы
	|ГДЕ
	|	УчастникиГруппы.Ссылка = &Ссылка
	|	И УчастникиГруппы.Пользователь = &Пользователь";
	ПользовательНайден = НЕ Запрос.Выполнить().Пустой();
	
	Если Включить = Неопределено Тогда
		Возврат ПользовательНайден;
	КонецЕсли;
	
	Если Включить И ПользовательНайден Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если НЕ Включить И НЕ ПользовательНайден Тогда
		Возврат Истина;
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
	ЭлементБлокировки.УстановитьЗначение("Ссылка", ГруппаДоступа);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		
		ГруппаДоступа = ГруппаДоступа.ПолучитьОбъект();
		
		Если НЕ УпрощенныйИнтерфейсНастройкиПравДоступа()
		   И НЕ ГруппаДоступа.ОсновнаяГруппаДоступаПоставляемогоПрофиля Тогда
			
			ГруппаДоступа.ОсновнаяГруппаДоступаПоставляемогоПрофиля = Истина;
		КонецЕсли;
		
		Если Включить Тогда
			ГруппаДоступа.Пользователи.Добавить().Пользователь = Пользователь;
		Иначе
			Отбор = Новый Структура("Пользователь", Пользователь);
			Строки = ГруппаДоступа.Пользователи.НайтиСтроки(Отбор);
			Для каждого Строка Из Строки Цикл
				ГруппаДоступа.Пользователи.Удалить(Строка);
			КонецЦикла;
		КонецЕсли;
		
		ГруппаДоступа.Записать();
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат Истина;
	
КонецФункции

// Для процедуры ЗаписатьНаборыЗначенийДоступаПриЗаписи.

// Перезаписывает наборы значений доступа проверяемого объекта
// в РегистрСведений.НаборыЗначенийДоступа, используя процедуру
// УправлениеДоступом.ЗаполнитьНаборыЗначенийДоступа().
//
// Процедура вызывается из УправлениеДоступомСлужебный.ЗаписатьНаборыЗначенийДоступа(),
// но может быть вызвана из любого места, например,
// при включении ограничения доступа на уровне записей.
//
// Вызывает процедуру прикладного разработчика
// УправлениеДоступомПереопределяемый.ПриИзмененииНаборовЗначенийДоступа(),
// которая используется для перезаписи зависимых наборов значений доступа.
//
// Параметры:
//  Объект       - ЛюбаяСсылка
//               - ОпределяемыйТип.ВладелецНаборовЗначенийДоступаОбъект -
//                 В случае вызова с клиента можно передать только ссылку, а нужен объект.
//                 Если получена ссылка, то по ней будет получен объект.
//  ОбновлениеИБ - Булево - если Истина, то необходимо выполнять запись данных, 
//                 не выполняя лишних, избыточных действий с данными.
//                 См. ОбновлениеИнформационнойБазы.ЗаписатьДанные.
//
Процедура ЗаписатьНаборыЗначенийДоступа(Знач Объект, ЕстьИзменения = Неопределено, ОбновлениеИБ = Ложь)
	
	УстановитьПривилегированныйРежим(Истина);
	
	// Если передача параметра Объект производилась с клиента на сервер,
	// то передавалась ссылка, и объект требуется получить.
	Объект = ?(Объект = Объект.Ссылка, Объект.ПолучитьОбъект(), Объект);
	СсылкаНаОбъект = Объект.Ссылка;
	ТипЗначенияОбъект = ТипЗнч(Объект);
	
	НаборыЗаписываются = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
		"ЗаписатьНаборыЗначенийДоступа").Получить(ТипЗначенияОбъект) <> Неопределено;
	
	Если НЕ НаборыЗаписываются Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неверные параметры.
			           |Тип объекта ""%1""
			           |не существует в подписках на события %2.'"),
			ТипЗначенияОбъект,
			"ЗаписатьНаборыЗначенийДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ВозможныеТипыОбъектов = УправлениеДоступомСлужебныйПовтИсп.ТипыПоляТаблицы(
		"РегистрСведений.НаборыЗначенийДоступа.Измерение.Объект");
	
	Если ВозможныеТипыОбъектов.Получить(ТипЗнч(СсылкаНаОбъект)) = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка при записи наборов значений доступа:
			           |в регистре сведений %1 в измерении %2
			           |не задан тип ""%3"".'"),
			"НаборыЗначенийДоступа",
			"Объект",
			ТипЗнч(СсылкаНаОбъект));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если УправлениеДоступом.ОграничиватьДоступНаУровнеЗаписей()
	   И Не УправлениеДоступом.ПроизводительныйВариант() Тогда
		
		Если Метаданные.НайтиПоТипу(ТипЗначенияОбъект).ТабличныеЧасти.Найти("НаборыЗначенийДоступа") = Неопределено Тогда
			
			Таблица = УправлениеДоступом.ТаблицаНаборыЗначенийДоступа();
			УправлениеДоступом.ЗаполнитьНаборыЗначенийДоступа(Объект, Таблица);
			
			УправлениеДоступом.ДобавитьНаборыЗначенийДоступа(
				Таблица, УправлениеДоступом.ТаблицаНаборыЗначенийДоступа(), Ложь, Истина);
		Иначе
			ТабличнаяЧастьЗаполняется = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
				"ЗаполнитьНаборыЗначенийДоступаТабличныхЧастей
				|ЗаполнитьНаборыЗначенийДоступаТабличныхЧастейДокументов").Получить(ТипЗначенияОбъект) <> Неопределено;
			
			Если НЕ ТабличнаяЧастьЗаполняется Тогда
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Неверные параметры.
					           |Тип объекта ""%1""
					           |не существует в подписках на события %2.'"),
					ТипЗначенияОбъект,
					"ЗаполнитьНаборыЗначенийДоступаТабличныхЧастей");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			// Записан объект с уже заполненной табличной частью НаборыЗначенийДоступа.
			Таблица = Объект.НаборыЗначенийДоступа.Выгрузить();
		КонецЕсли;
		
		ПодготовитьНаборыЗначенийДоступаКЗаписи(СсылкаНаОбъект, Таблица, Истина);
		
		Данные = Новый Структура;
		Данные.Вставить("МенеджерРегистра",   РегистрыСведений.НаборыЗначенийДоступа);
		Данные.Вставить("ФиксированныйОтбор", Новый Структура("Объект", СсылкаНаОбъект));
		Данные.Вставить("НовыеЗаписи",        Таблица);
		Данные.Вставить("ОбновлениеИБ",       ОбновлениеИБ);
		
		НачатьТранзакцию();
		Попытка
			ОбновитьНаборыЗаписей(Данные, ЕстьИзменения);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		Если ЕстьИзменения = Истина Тогда
			ПриИзмененииНаборовЗначенийДоступа(СсылкаНаОбъект, ОбновлениеИБ);
		КонецЕсли;
	Иначе
		Запрос = Новый Запрос(
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА КАК ЗначениеИстина
		|ИЗ
		|	РегистрСведений.НаборыЗначенийДоступа КАК НаборыЗначенийДоступа
		|ГДЕ
		|	НаборыЗначенийДоступа.Объект = &СсылкаНаОбъект");
		
		Запрос.УстановитьПараметр("СсылкаНаОбъект", СсылкаНаОбъект);
		
		// АПК:1328-выкл - №648.1.1 Допустимо чтение без предварительной
		// управляемой разделяемой блокировки объекта, так как только
		// для RLS в формате БСП 2.х (устарело) и ни разу не вызывало проблем.
		Если НЕ Запрос.Выполнить().Пустой() Тогда
		// АПК:1328-вкл.
			
			// Очистка устаревшего набора.
			// Запись нового набора будет выполнена регламентным заданием,
			// после включения ограничения на уровне записей.
			НаборЗаписей = РегистрыСведений.НаборыЗначенийДоступа.СоздатьНаборЗаписей();
			НаборЗаписей.Отбор.Объект.Установить(СсылкаНаОбъект);
			НаборЗаписей.Записать();
			ЕстьИзменения = Истина;
			
			// Очистка устаревших зависимых наборов.
			ПриИзмененииНаборовЗначенийДоступа(СсылкаНаОбъект, ОбновлениеИБ);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаписатьЗависимыеНаборыЗначенийДоступаПриЗаписи.

// Перезаписывает наборы значений доступа зависимых объектов.
//
//  Процедура вызывается из УправлениеДоступомСлужебный.ЗаписатьЗависимыеНаборыЗначенийДоступа(),
// состав типов подписки дополняет (без пересечения) состав типов подписки ЗаписатьНаборыЗначенийДоступа,
// теми типами, для которых выполнять запись наборов в регистр сведений НаборыЗначенийДоступа
// не требуется, но сами наборы входят в состав других наборов, например, наборы некоторых файлов
// из справочника "Файлы" могут входить в состав некоторых бизнес-процессов "Задание", созданных
// на основании файлов, при этом наборы файлов записывать в регистр не требуется.
//
// Вызывает процедуру прикладного разработчика
// УправлениеДоступомПереопределяемый.ПриИзмененииНаборовЗначенийДоступа(),
// которая используется для перезаписи зависимых наборов значений доступа,
// то есть организуется рекурсия.
//
// Параметры:
//  Объект       - ЛюбаяСсылка
//               - ОпределяемыйТип.ВладелецНаборовЗначенийДоступаОбъект -
//                 В случае вызова с клиента можно передать только ссылку, а нужен объект.
//                 Если получена ссылка, то по ней будет получен объект.
//
//  ОбновлениеИБ - Булево - если Истина, то необходимо выполнять запись данных, 
//                 не выполняя лишних, избыточных действий с данными.
//                 См. ОбновлениеИнформационнойБазы.ЗаписатьДанные.
//
Процедура ЗаписатьЗависимыеНаборыЗначенийДоступа(Знач Объект, ОбновлениеИБ = Ложь)
	
	УстановитьПривилегированныйРежим(Истина);
	
	// Если передача параметра Объект производилась с клиента на сервер,
	// то передавалась ссылка, и объект требуется получить.
	Объект = ?(Объект = Объект.Ссылка, Объект.ПолучитьОбъект(), Объект);
	СсылкаНаОбъект = Объект.Ссылка;
	ТипЗначенияОбъект = ТипЗнч(Объект);
	
	ЭтоВедущийОбъект = УправлениеДоступомСлужебныйПовтИсп.ТипыОбъектовВПодпискахНаСобытия(
		"ЗаписатьЗависимыеНаборыЗначенийДоступа").Получить(ТипЗначенияОбъект) <> Неопределено;
	
	Если НЕ ЭтоВедущийОбъект Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неверные параметры.
			           |Тип объекта ""%1""
			           |не существует в подписке на события %2.'"),
			ТипЗначенияОбъект,
			"ЗаписатьЗависимыеНаборыЗначенийДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ПриИзмененииНаборовЗначенийДоступа(СсылкаНаОбъект, ОбновлениеИБ);
	
КонецПроцедуры

// Для процедур ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации и
// ЗаполнитьОбработчикиРазделенныхДанных.

// Проверяет были ли изменения неразделенных данных для какой-нибудь области данных.
Функция ЕстьИзмененияПараметровОграниченияДоступа()
	
	УстановитьПривилегированныйРежим(Истина);
	
	Параметры = Новый Массив;
	Параметры.Добавить("СтандартныеПодсистемы.УправлениеДоступом.ОбъектыМетаданныхПравРолей");
	Параметры.Добавить("СтандартныеПодсистемы.УправлениеДоступом.ВозможныеПраваДляНастройкиПравОбъектов");
	Параметры.Добавить("СтандартныеПодсистемы.УправлениеДоступом.ОписаниеПоставляемыхПрофилей");
	Параметры.Добавить("СтандартныеПодсистемы.УправлениеДоступом.ПредопределенныеПрофилиГруппДоступа");
	Параметры.Добавить("СтандартныеПодсистемы.УправлениеДоступом.ТипыГруппИЗначенийДоступа");
	Параметры.Добавить("СтандартныеПодсистемы.УправлениеДоступом.ВерсияТекстовОграниченияДоступа");
	
	Для Каждого Параметр Из Параметры Цикл
		
		ПоследниеИзменения = СтандартныеПодсистемыСервер.ИзмененияПараметраРаботыПрограммы(Параметр);
		
		Если ПоследниеИзменения = Неопределено
		 ИЛИ ПоследниеИзменения.Количество() > 0 Тогда
			
			Возврат Истина;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для процедуры ОбновитьРолиПользователей.

// Для тестирования.
//
// Параметры:
//  ДополнительныеРоли - Соответствие из КлючИЗначение:
//    * Ключ     - Строка - имя роли, которую можно назначить администратору.
//    * Значение - Булево - Истина.
//
Процедура ПриПодготовкеДополнительныхРолейАдминистратора(ДополнительныеРоли)
	Возврат;
КонецПроцедуры

Функция ТекущиеСвойстваПользователей(МассивПользователей)
	
	Запрос = Новый Запрос;
	
	Запрос.УстановитьПараметр("ГруппаДоступаАдминистраторы",
		УправлениеДоступом.ГруппаДоступаАдминистраторы());
	
	Запрос.УстановитьПараметр("ПустойИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Если МассивПользователей = Неопределено Тогда
		Запрос.Текст =
		"ВЫБРАТЬ
		|	Пользователи.Ссылка КАК Пользователь,
		|	Пользователи.ИдентификаторПользователяИБ
		|ПОМЕСТИТЬ ПроверяемыеПользователи
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|ГДЕ
		|	Пользователи.Служебный = ЛОЖЬ
		|	И Пользователи.ИдентификаторПользователяИБ <> &ПустойИдентификатор
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ВнешниеПользователи.Ссылка,
		|	ВнешниеПользователи.ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|ГДЕ
		|	ВнешниеПользователи.ИдентификаторПользователяИБ <> &ПустойИдентификатор";
		
	ИначеЕсли ТипЗнч(МассивПользователей) = Тип("Тип") Тогда
		Если Метаданные.НайтиПоТипу(МассивПользователей) = Метаданные.Справочники.ВнешниеПользователи Тогда
			Запрос.Текст =
			"ВЫБРАТЬ
			|	ВнешниеПользователи.Ссылка КАК Пользователь,
			|	ВнешниеПользователи.ИдентификаторПользователяИБ
			|ПОМЕСТИТЬ ПроверяемыеПользователи
			|ИЗ
			|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
			|ГДЕ
			|	ВнешниеПользователи.ИдентификаторПользователяИБ <> &ПустойИдентификатор";
		Иначе
			Запрос.Текст =
			"ВЫБРАТЬ
			|	Пользователи.Ссылка КАК Пользователь,
			|	Пользователи.ИдентификаторПользователяИБ
			|ПОМЕСТИТЬ ПроверяемыеПользователи
			|ИЗ
			|	Справочник.Пользователи КАК Пользователи
			|ГДЕ
			|	Пользователи.Служебный = ЛОЖЬ
			|	И Пользователи.ИдентификаторПользователяИБ <> &ПустойИдентификатор";
		КонецЕсли;
	Иначе
		ИсходныеПользователи = Новый ТаблицаЗначений;
		ИсходныеПользователи.Колонки.Добавить("Пользователь", Новый ОписаниеТипов(
			"СправочникСсылка.Пользователи, СправочникСсылка.ВнешниеПользователи"));
		
		Для каждого Пользователь Из МассивПользователей Цикл
			ИсходныеПользователи.Добавить().Пользователь = Пользователь;
		КонецЦикла;
		
		Запрос.УстановитьПараметр("ИсходныеПользователи", ИсходныеПользователи);
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ИсходныеПользователи.Пользователь
		|ПОМЕСТИТЬ ИсходныеПользователи
		|ИЗ
		|	&ИсходныеПользователи КАК ИсходныеПользователи
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	Пользователи.Ссылка КАК Пользователь,
		|	Пользователи.ИдентификаторПользователяИБ
		|ПОМЕСТИТЬ ПроверяемыеПользователи
		|ИЗ
		|	Справочник.Пользователи КАК Пользователи
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ИсходныеПользователи КАК ИсходныеПользователи
		|		ПО Пользователи.Ссылка = ИсходныеПользователи.Пользователь
		|			И (Пользователи.Служебный = ЛОЖЬ)
		|			И (Пользователи.ИдентификаторПользователяИБ <> &ПустойИдентификатор)
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ВнешниеПользователи.Ссылка,
		|	ВнешниеПользователи.ИдентификаторПользователяИБ
		|ИЗ
		|	Справочник.ВнешниеПользователи КАК ВнешниеПользователи
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ИсходныеПользователи КАК ИсходныеПользователи
		|		ПО ВнешниеПользователи.Ссылка = ИсходныеПользователи.Пользователь
		|			И (ВнешниеПользователи.ИдентификаторПользователяИБ <> &ПустойИдентификатор)";
	КонецЕсли;
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	Пользователи.Ссылка КАК Пользователь,
	|	Пользователи.ИдентификаторПользователяИБ
	|ИЗ
	|	Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи
	|		ПО (ГруппыДоступаПользователи.Ссылка = &ГруппаДоступаАдминистраторы)
	|			И ГруппыДоступаПользователи.Пользователь = Пользователи.Ссылка
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ПроверяемыеПользователи.Пользователь,
	|	ПроверяемыеПользователи.ИдентификаторПользователяИБ
	|ИЗ
	|	ПроверяемыеПользователи КАК ПроверяемыеПользователи
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ПроверяемыеПользователи.Пользователь КАК Пользователь,
	|	ГруппыДоступаПользователи.Ссылка.Профиль КАК Профиль
	|ПОМЕСТИТЬ ПрофилиПользователей
	|ИЗ
	|	ПроверяемыеПользователи КАК ПроверяемыеПользователи
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО ПроверяемыеПользователи.Пользователь = СоставыГруппПользователей.Пользователь
	|			И (СоставыГруппПользователей.Используется)
	|			И (&ИсключитьВнешнихПользователей)
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|		ПО (СоставыГруппПользователей.ГруппаПользователей = ГруппыДоступаПользователи.Пользователь)
	|			И (НЕ ГруппыДоступаПользователи.Ссылка.ПометкаУдаления)
	|			И (НЕ ГруппыДоступаПользователи.Ссылка.Профиль.ПометкаУдаления)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ПрофилиПользователей.Пользователь,
	|	Роли.Роль КАК РольСсылка,
	|	Роли.Роль.Имя КАК Роль
	|ИЗ
	|	ПрофилиПользователей КАК ПрофилиПользователей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК Роли
	|		ПО (Роли.Ссылка = ПрофилиПользователей.Профиль)
	|ГДЕ
	|	Роли.Роль <> НЕОПРЕДЕЛЕНО";
	
	Запрос.Текст = Запрос.Текст + "
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|" + ТекстЗапроса;
	
	Если Константы.ИспользоватьВнешнихПользователей.Получить() Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ИсключитьВнешнихПользователей", "ИСТИНА");
	Иначе
		// @query-part-2
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ИсключитьВнешнихПользователей",
			"ТИПЗНАЧЕНИЯ(ПроверяемыеПользователи.Пользователь) = ТИП(Справочник.Пользователи)");
	КонецЕсли;
	
	РезультатыЗапросов = Запрос.ВыполнитьПакет();
	ПоследнийРезультат = РезультатыЗапросов.Количество()-1;
	Итог = Новый Структура;
	
	Итог.Вставить("Администраторы", Новый Соответствие);
	
	Для каждого Строка Из РезультатыЗапросов[ПоследнийРезультат-3].Выгрузить() Цикл
		Итог.Администраторы.Вставить(Строка.Пользователь, Истина);
	КонецЦикла;
	
	Итог.Вставить("ИдентификаторыПользователейИБ", РезультатыЗапросов[ПоследнийРезультат-2].Выгрузить());
	
	Если ПользователиСлужебный.СостояниеДоВызоваАвторизоватьТекущегоПользователя(Истина) Тогда
		Отбор = Новый Структура("ИдентификаторПользователяИБ",
			ПользователиИнформационнойБазы.ТекущийПользователь().УникальныйИдентификатор);
		НайденныеСтроки = Итог.ИдентификаторыПользователейИБ.НайтиСтроки(Отбор);
		Если ЗначениеЗаполнено(НайденныеСтроки) Тогда
			Итог.Администраторы.Вставить(НайденныеСтроки[0].Пользователь, Истина);
		КонецЕсли;
	КонецЕсли;
	
	Итог.Вставить("РолиПользователей", РезультатыЗапросов[ПоследнийРезультат].Выгрузить());
	Итог.РолиПользователей.Индексы.Добавить("Пользователь");
	
	РолиВсехПользователей = Итог.РолиПользователей.Скопировать(, "РольСсылка");
	РолиВсехПользователей.Свернуть("РольСсылка");
	ИдентификаторыРолей = РолиВсехПользователей.ВыгрузитьКолонку("РольСсылка");
	ПроверитьАктуальностьНовыхРолейПользователейВСеансе(ИдентификаторыРолей);
	МетаданныеРолей = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(ИдентификаторыРолей, Ложь);
	
	Для Каждого Строка Из Итог.РолиПользователей Цикл
		МетаданныеРоли = МетаданныеРолей.Получить(Строка.РольСсылка);
		Если ТипЗнч(МетаданныеРоли) = Тип("ОбъектМетаданных") Тогда
			Строка.Роль = МетаданныеРоли.Имя;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Итог;
	
КонецФункции

// Для функции ТекущиеСвойстваПользователей.
Процедура ПроверитьАктуальностьНовыхРолейПользователейВСеансе(ИдентификаторыРолей)
	
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ИдентификаторыРолей";
	ЗначениеСеанса = УправлениеДоступомСлужебныйПовтИсп.ИдентификаторыРолейСеанса();
	
	ТекущееЗначение = ПоследниеИдентификаторыРолей(ИмяПараметра);
	
	Если ТекущееЗначение.ХешСумма = ЗначениеСеанса.ХешСумма Тогда
		Возврат;
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
	ЭлементБлокировки.УстановитьЗначение("ВерсияРасширений", Справочники.ВерсииРасширений.ПустаяСсылка());
	ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		УжеИзменен = Ложь;
		ТекущееЗначение = ПоследниеИдентификаторыРолей(ИмяПараметра, УжеИзменен);
		Если ТекущееЗначение.ХешСумма <> ЗначениеСеанса.ХешСумма Тогда
			ПропуститьОбновление = Ложь;
			Если УжеИзменен Тогда
				Попытка
					ПроверитьАктуальностьМетаданных();
				Исключение
					ИдентификаторыСеанса  = ЗначениеСеанса.ИдентификаторыРолей;
					ТекущиеИдентификаторы = ТекущееЗначение.ИдентификаторыРолей;
					Для Каждого ИдентификаторРоли Из ИдентификаторыРолей Цикл
						ЕстьВТекущих = ТекущиеИдентификаторы.Получить(ИдентификаторРоли) <> Неопределено;
						ЕстьВСеансе  = ИдентификаторыСеанса.Получить(ИдентификаторРоли) <> Неопределено;
						Если ЕстьВТекущих <> ЕстьВСеансе Тогда
							ВызватьИсключение;
						КонецЕсли;
					КонецЦикла;
					ПропуститьОбновление = Истина;
				КонецПопытки;
			КонецЕсли;
			Если Не ПропуститьОбновление Тогда
				СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, ЗначениеСеанса, Истина);
			КонецЕсли;
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// См. УправлениеДоступомСлужебныйПовтИсп.ИдентификаторыРолейСеанса
Функция ПоследниеИдентификаторыРолей(ИмяПараметра, УжеИзменен = Ложь)
	
	Значение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
	Если ТипЗнч(Значение) <> Тип("ФиксированнаяСтруктура")
	 Или Не Значение.Свойство("ХешСумма")
	 Или Не Значение.Свойство("ИдентификаторыРолей")
	 Или ТипЗнч(Значение.ИдентификаторыРолей) <> Тип("ФиксированноеСоответствие") Тогда
		
		Значение = Новый Структура("ХешСумма, ИдентификаторыРолей", "", Новый Соответствие);
	КонецЕсли;
	
	Возврат Значение;
	
КонецФункции

// Параметры:
//  НовыеРолиПользователей - ТаблицаЗначений
// 
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Пользователь - СправочникСсылка.Пользователи
//                  - СправочникСсылка.ВнешниеПользователи
//   * РольСсылка   - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                  - СправочникСсылка.ИдентификаторыОбъектовРасширений
//   * Роль         - Строка - имя роли.
//
Функция НовыеНекорректныеРоли(НовыеРолиПользователей)
	
	Результат = НовыеРолиПользователей.Скопировать(
		Новый Массив, "Пользователь, Роль, РольСсылка");
	
	Результат.Колонки.Добавить("ЭтоНенайденнаяРоль", Новый ОписаниеТипов("Булево"));
	
	Возврат Результат;
	
КонецФункции

// Параметры:
//  НекорректныеРоли - см. НовыеНекорректныеРоли
//  ОписаниеРоли - СтрокаТаблицыЗначений
//  Пользователь - СправочникСсылка.Пользователи
//               - СправочникСсылка.ВнешниеПользователи
//
Процедура ДобавитьНекорректнуюРоль(НекорректныеРоли, ОписаниеРоли, Пользователь, ЭтоНенайденнаяРоль)
	
	НоваяСтрока = НекорректныеРоли.Добавить();
	ЗаполнитьЗначенияСвойств(НоваяСтрока, ОписаниеРоли);
	НоваяСтрока.Пользователь = Пользователь;
	НоваяСтрока.ЭтоНенайденнаяРоль = ЭтоНенайденнаяРоль;
	
КонецПроцедуры

// Параметры:
//  НекорректныеРоли - см. НовыеНекорректныеРоли
//
Процедура ЗарегистрироватьНекорректныеРоли(НекорректныеРоли)
	
	Если Не ЗначениеЗаполнено(НекорректныеРоли) Тогда
		Возврат;
	КонецЕсли;
	
	ПрофилиНекорректныхРолей = ПрофилиПользователейСРолями(НекорректныеРоли);
	ПрофилиНекорректныхРолей.Индексы.Добавить("Пользователь, РольСсылка");
	Отбор = Новый Структура("Пользователь, РольСсылка");
	
	Для Каждого НекорректнаяРоль Из НекорректныеРоли Цикл
		ЗаполнитьЗначенияСвойств(Отбор, НекорректнаяРоль);
		Строки = ПрофилиНекорректныхРолей.НайтиСтроки(Отбор);
		Для Каждого Строка Из Строки Цикл
			Если НекорректнаяРоль.ЭтоНенайденнаяРоль Тогда
				ЗарегистрироватьНенайденнуюРоль(НекорректнаяРоль, Строка.Профиль);
			Иначе
				ЗарегистрироватьНедоступнуюРоль(НекорректнаяРоль, Строка.Профиль);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

Процедура ЗарегистрироватьНедоступнуюРоль(ОписаниеРоли, Профиль)
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Управление доступом.Роль недоступна пользователю'",
		     ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Ошибка,
		Метаданные.Справочники.ПрофилиГруппДоступа,
		Профиль,
		СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При обновлении ролей пользователя ""%1""
			           |роль ""%2""
			           |%3
			           |профиля групп доступа ""%4""
			           |%5
			           |недоступна пользователю.'"),
			Строка(ОписаниеРоли.Пользователь),
			ОписаниеРоли.Роль,
			ПолучитьНавигационнуюСсылку(ОписаниеРоли.РольСсылка),
			Строка(Профиль),
			ПолучитьНавигационнуюСсылку(Профиль)),
		РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
	
КонецПроцедуры

Процедура ЗарегистрироватьНенайденнуюРоль(ОписаниеРоли, Профиль)
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Управление доступом.Роль не найдена в метаданных'",
		     ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Ошибка,
		Метаданные.Справочники.ПрофилиГруппДоступа,
		Профиль,
		СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При обновлении ролей пользователя ""%1""
			           |роль ""%2""
			           |%3
			           |профиля групп доступа ""%4""
			           |%5
			           |не существует в метаданных.'"),
			Строка(ОписаниеРоли.Пользователь),
			ОписаниеРоли.Роль,
			ПолучитьНавигационнуюСсылку(ОписаниеРоли.РольСсылка),
			Строка(Профиль),
			ПолучитьНавигационнуюСсылку(Профиль)),
		РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
	
КонецПроцедуры

Процедура ОбновитьРолиПользователейИБ(ОбновляемыеПользователиИБ, ПарольПользователяСервиса)
	
	СообщатьВМенеджерСервиса = ОбщегоНазначения.РазделениеВключено()
		И ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаВМоделиСервиса.ПользователиВМоделиСервиса");
	
	Для Каждого КлючИЗначение Из ОбновляемыеПользователиИБ Цикл
		РолиДляДобавления  = КлючИЗначение.Значение.РолиДляДобавления;
		РолиДляУдаления    = КлючИЗначение.Значение.РолиДляУдаления;
		ПользовательИБ     = КлючИЗначение.Значение.ПользовательИБ;
		ПользовательСсылка = КлючИЗначение.Значение.ПользовательСсылка;
		
		Если СообщатьВМенеджерСервиса Тогда
			БылиПолныеПрава = ПользовательИБ.Роли.Содержит(Метаданные.Роли.ПолныеПрава);
			БылиПраваНаВход = Пользователи.ЕстьПраваДляВходаВПрограмму(ПользовательИБ,, Ложь);
		КонецЕсли;
		
		Для Каждого КлючИЗначение Из РолиДляДобавления Цикл
			ПользовательИБ.Роли.Добавить(Метаданные.Роли[КлючИЗначение.Ключ]);
		КонецЦикла;
		
		Для Каждого КлючИЗначение Из РолиДляУдаления Цикл
			Если ТипЗнч(КлючИЗначение.Значение) = Тип("ОбъектМетаданных") Тогда
				Роль = КлючИЗначение.Значение;
			Иначе
				Роль = Метаданные.Роли[КлючИЗначение.Ключ];
			КонецЕсли;
			ПользовательИБ.Роли.Удалить(Роль);
		КонецЦикла;
		
		НачатьТранзакцию();
		Попытка
			ЗаписатьПользователяПриОбновленииРолей(ПользовательСсылка,
				ПользовательИБ, БылиПолныеПрава, БылиПраваНаВход, ПарольПользователяСервиса);
			
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

Процедура ОтключитьУВсехРасширенийФлажокИспользоватьОсновныеРолиДляВсехПользователей()
	
	Отбор = Новый Структура("ИспользоватьОсновныеРолиДляВсехПользователей", Истина);
	Расширения = РасширенияКонфигурации.Получить(Отбор);
	РазделениеВключено = ОбщегоНазначения.РазделениеВключено();
	ТекущаяОбластьДействия = ?(ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных(),
		ОбластьДействияРасширенияКонфигурации.РазделениеДанных,
		ОбластьДействияРасширенияКонфигурации.ИнформационнаяБаза);
	
	Для Каждого Расширение Из Расширения Цикл
		Если Не Расширение.ИспользоватьОсновныеРолиДляВсехПользователей
		 Или РазделениеВключено
		   И Расширение.ОбластьДействия <> ТекущаяОбластьДействия Тогда
			Продолжить;
		КонецЕсли;
		Справочники.ВерсииРасширений.ОтключитьПредупрежденияБезопасности(Расширение);
		Справочники.ВерсииРасширений.ОтключитьИспользованиеОсновныхРолейДляВсехПользователей(Расширение);
		// АПК:280-выкл - №499.3.4 Допустимо пропустить обработку исключения, так как
		// это не останавливающая операция и будет выполнена успешно при очередном обновлении ролей.
		// Запись расширения не является транзакционной операцией, поэтому не создает ошибок в транзакции.
		Попытка
			Расширение.Записать();
		Исключение
			// Обработка не требуется.
		КонецПопытки;
		// АПК:280-вкл.
	КонецЦикла;
	
КонецПроцедуры

// Параметры:
//  НекорректныеРоли - см. НовыеНекорректныеРоли
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Пользователь - СправочникСсылка.Пользователи
//                  - СправочникСсылка.ВнешниеПользователи
//   * РольСсылка   - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                  - СправочникСсылка.ИдентификаторыОбъектовРасширений
//   * Профиль      - СправочникСсылка.ПрофилиГруппДоступа
// 
Функция ПрофилиПользователейСРолями(НекорректныеРоли)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("НекорректныеРоли", НекорректныеРоли);
	Запрос.УстановитьПараметр("ПустойИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	НекорректныеРоли.Пользователь КАК Пользователь,
	|	НекорректныеРоли.РольСсылка КАК РольСсылка
	|ПОМЕСТИТЬ НекорректныеРоли
	|ИЗ
	|	&НекорректныеРоли КАК НекорректныеРоли
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	НекорректныеРоли.Пользователь КАК Пользователь,
	|	НекорректныеРоли.РольСсылка КАК РольСсылка,
	|	Роли.Ссылка КАК Профиль
	|ИЗ
	|	НекорректныеРоли КАК НекорректныеРоли
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ПО (СоставыГруппПользователей.Пользователь = НекорректныеРоли.Пользователь)
	|			И (СоставыГруппПользователей.Используется)
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК ГруппыДоступаПользователи
	|		ПО (ГруппыДоступаПользователи.Пользователь = СоставыГруппПользователей.ГруппаПользователей)
	|			И (НЕ ГруппыДоступаПользователи.Ссылка.ПометкаУдаления)
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК Роли
	|		ПО (Роли.Ссылка = ГруппыДоступаПользователи.Ссылка.Профиль)
	|			И (НЕ Роли.Ссылка.ПометкаУдаления)
	|			И (Роли.Роль = НекорректныеРоли.РольСсылка)";
	
	Возврат Запрос.Выполнить().Выгрузить();
	
КонецФункции

// Для процедуры ОбновитьРолиПользователейИБ.
Процедура ЗаписатьПользователяПриОбновленииРолей(ПользовательСсылка, ПользовательИБ,
			БылиПолныеПрава, БылиПраваНаВход, ПарольПользователяСервиса)
	
	ПользователиСлужебный.ЗаписатьПользователяИнформационнойБазы(ПользовательИБ,
		ТипЗнч(ПользовательСсылка) = Тип("СправочникСсылка.ВнешниеПользователи"),
		ПользовательСсылка,
		Ложь);
	
	Если БылиПолныеПрава = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ЕстьПолныеПрава = ПользовательИБ.Роли.Содержит(Метаданные.Роли.ПолныеПрава);
	ЕстьПраваНаВход = Пользователи.ЕстьПраваДляВходаВПрограмму(ПользовательИБ,, Ложь);
	
	Если ЕстьПолныеПрава = БылиПолныеПрава
	   И ЕстьПраваНаВход = БылиПраваНаВход
	 Или Не Пользователи.ВходВПрограммуРазрешен(ПользовательИБ) Тогда
		Возврат;
	КонецЕсли;
	
	МодульПользователиСлужебныйВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("ПользователиСлужебныйВМоделиСервиса");
	
	Если ЕстьПолныеПрава <> БылиПолныеПрава Тогда
		Если ПарольПользователяСервиса = Неопределено Тогда
			Если ОбщегоНазначения.ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
				МодульРаботаВМоделиСервиса = ОбщегоНазначения.ОбщийМодуль("РаботаВМоделиСервиса");
				СеансЗапущенБезРазделителей = МодульРаботаВМоделиСервиса.СеансЗапущенБезРазделителей();
			Иначе
				СеансЗапущенБезРазделителей = Истина;
			КонецЕсли;
			Если Не СеансЗапущенБезРазделителей Тогда
				УпрощенныйИнтерфейс = УпрощенныйИнтерфейсНастройкиПравДоступа();
				
				Если БылиПолныеПрава Тогда
					Шаблон = НСтр("ru = 'Для отключения административного доступа пользователю ""%1"" требуется пароль текущего пользователя сервиса ""%2"".'");
				Иначе
					Шаблон = НСтр("ru = 'Для установки административного доступа пользователю ""%1"" требуется пароль текущего пользователя сервиса ""%2"".'");
				КонецЕсли;
				
				Если БылиПолныеПрава И УпрощенныйИнтерфейс Тогда
					Уточнение = НСтр("ru = 'Это действие может быть выполнено только в карточке пользователя при отключении ему профиля Администратор.'");
				ИначеЕсли Не БылиПолныеПрава И УпрощенныйИнтерфейс Тогда
					Уточнение = НСтр("ru = 'Это действие может быть выполнено только в карточке пользователя при включении ему профиля Администратор.'");
				ИначеЕсли БылиПолныеПрава Тогда
					Уточнение = НСтр("ru = 'Это действие может быть выполнено только в карточке группы доступа Администраторы или в карточке пользователя при исключении его из группы доступа Администраторы.'");
				Иначе
					Уточнение = НСтр("ru = 'Это действие может быть выполнено только в карточке группы доступа Администраторы или в карточке пользователя при включении его в группу доступа Администраторы.'");
				КонецЕсли;
				
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Шаблон,
					ПользовательИБ.Имя, ПользователиИнформационнойБазы.ТекущийПользователь().Имя);
				ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС + Уточнение;
				
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		Иначе
			МодульПользователиСлужебныйВМоделиСервиса.ЗаписатьПользователяСервиса(ПользовательСсылка,
				Ложь, ПарольПользователяСервиса);
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Если ЕстьПраваНаВход <> БылиПраваНаВход Тогда
		МодульПользователиСлужебныйВМоделиСервиса.СообщитьИзмененЗапускПриложения(ПользовательСсылка,
			ПользовательИБ);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ТекстЗапросаВыбораИзменений.

Функция КлючИЗначение(Структура)
	
	Для Каждого КлючИЗначение Из Структура Цикл
		Возврат КлючИЗначение;
	КонецЦикла;
	
	Возврат "";
	
КонецФункции

// Для процедур ОбновитьНаборЗаписей, ОбновитьНовыеЗаписиНабораПоРазличнымНовымЗаписям.

Процедура ЗаписатьОбъектИлиНаборЗаписей(Данные, ОбъектИлиНаборЗаписей)
	
	Если Данные.ОбновлениеИБ Тогда
		ОбновлениеИнформационнойБазы.ЗаписатьДанные(ОбъектИлиНаборЗаписей);
	Иначе
		ОбъектИлиНаборЗаписей.Записать();
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ОбновитьНаборЗаписей и ОбновитьНаборыЗаписей.

Функция ГруппаПараметровИзмеренияОбработана(ИмяИзмерения, ЗначенияИзмерения)
	
	Если ИмяИзмерения = Неопределено Тогда
		ЗначенияИзмерения = Неопределено;
		
	ИначеЕсли ЗначенияИзмерения = Неопределено Тогда
		ИмяИзмерения = Неопределено;
		
	ИначеЕсли ТипЗнч(ЗначенияИзмерения) <> Тип("Массив")
	        И ТипЗнч(ЗначенияИзмерения) <> Тип("ФиксированныйМассив") Тогда
		
		ЗначениеИзмерения = ЗначенияИзмерения;
		ЗначенияИзмерения = Новый Массив;
		ЗначенияИзмерения.Добавить(ЗначениеИзмерения);
		
	ИначеЕсли ЗначенияИзмерения.Количество() = 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

Процедура УпорядочитьГруппыПараметровИзмерений(Данные)
	
	Если Данные.ИмяВторогоИзмерения = Неопределено Тогда
		Данные.ИмяВторогоИзмерения       = Данные.ИмяТретьегоИзмерения;
		Данные.ЗначенияВторогоИзмерения  = Данные.ЗначенияТретьегоИзмерения;
		Данные.ИмяТретьегоИзмерения      = Неопределено;
		Данные.ЗначенияТретьегоИзмерения = Неопределено;
	КонецЕсли;
	
	Если Данные.ИмяПервогоИзмерения = Неопределено Тогда
		Данные.ИмяПервогоИзмерения       = Данные.ИмяВторогоИзмерения;
		Данные.ЗначенияПервогоИзмерения  = Данные.ЗначенияВторогоИзмерения;
		Данные.ИмяВторогоИзмерения       = Данные.ИмяТретьегоИзмерения;
		Данные.ЗначенияВторогоИзмерения  = Данные.ЗначенияТретьегоИзмерения;
		Данные.ИмяТретьегоИзмерения      = Неопределено;
		Данные.ЗначенияТретьегоИзмерения = Неопределено;
	КонецЕсли;
	
	Если Данные.ЗначенияВторогоИзмерения  <> Неопределено
	   И Данные.ЗначенияТретьегоИзмерения <> Неопределено
	   И Данные.ЗначенияВторогоИзмерения.Количество()
	   > Данные.ЗначенияТретьегоИзмерения.Количество() Тогда
		
		ИмяИзмерения      = Данные.ИмяВторогоИзмерения;
		ЗначенияИзмерения = Данные.ЗначенияВторогоИзмерения;
		
		Данные.ИмяВторогоИзмерения       = Данные.ИмяТретьегоИзмерения;
		Данные.ЗначенияВторогоИзмерения  = Данные.ЗначенияТретьегоИзмерения;
		Данные.ИмяТретьегоИзмерения      = ИмяИзмерения;
		Данные.ЗначенияТретьегоИзмерения = ЗначенияИзмерения;
	КонецЕсли;
	
	Если Данные.ЗначенияПервогоИзмерения <> Неопределено
	   И Данные.ЗначенияВторогоИзмерения <> Неопределено
	   И Данные.ЗначенияПервогоИзмерения.Количество()
	   > Данные.ЗначенияВторогоИзмерения.Количество() Тогда
		
		ИмяИзмерения      = Данные.ИмяПервогоИзмерения;
		ЗначенияИзмерения = Данные.ЗначенияПервогоИзмерения;
		
		Данные.ИмяПервогоИзмерения      = Данные.ИмяВторогоИзмерения;
		Данные.ЗначенияПервогоИзмерения = Данные.ЗначенияВторогоИзмерения;
		Данные.ИмяВторогоИзмерения      = ИмяИзмерения;
		Данные.ЗначенияВторогоИзмерения = ЗначенияИзмерения;
	КонецЕсли;
	
КонецПроцедуры

Функция ПоляНабораЗаписей(НаборЗаписей)
	
	ПоляСравнения = "";
	Таблица = НаборЗаписей.Выгрузить(Новый Массив);
	Для каждого Колонка Из Таблица.Колонки Цикл
		ПоляСравнения = ПоляСравнения + "," + Колонка.Имя;
	КонецЦикла;
	ПоляСравнения = Сред(ПоляСравнения, 2);
	
	Возврат ПоляСравнения;
	
КонецФункции

Процедура ОбновитьНовыеЗаписиНабораПоВсемНовымЗаписям(Знач Данные, Знач Отбор, Знач СписокПолей,
				Знач ИмяИзмерения, Знач ЗначенияИзмерения, ЕстьИзменения)
	
	ЗаблокироватьОбластьНабораЗаписей(Данные.НаборЗаписей, Данные.ПолноеИмяРегистра);
	
	Данные.НаборЗаписей.Прочитать();
	НовыеЗаписиНабора = Данные.НаборЗаписей.Выгрузить();
	НовыеЗаписиНабора.Индексы.Добавить(СписокПолей);
	
	Для каждого Значение Из ЗначенияИзмерения Цикл
		Отбор[ИмяИзмерения] = Значение;
		Для каждого НайденнаяЗапись Из НовыеЗаписиНабора.НайтиСтроки(Отбор) Цикл
			НовыеЗаписиНабора.Удалить(НайденнаяЗапись);
		КонецЦикла;
		Для каждого НайденнаяЗапись Из Данные.НовыеЗаписи.НайтиСтроки(Отбор) Цикл
			ЗаполнитьЗначенияСвойств(НовыеЗаписиНабора.Добавить(), НайденнаяЗапись);
		КонецЦикла;
	КонецЦикла;
	
	ТекущиеДанные = Новый Структура("НаборЗаписей, ПоляСравнения,
		|ТолькоПроверка, ДополнительныеСвойства, ОбновлениеИБ");
	ЗаполнитьЗначенияСвойств(ТекущиеДанные, Данные);
	ТекущиеДанные.Вставить("НовыеЗаписи", НовыеЗаписиНабора);
	ТекущиеДанные.Вставить("НаборЗаписейПрочитан", Истина);
	
	ОбновитьНаборЗаписей(ТекущиеДанные, ЕстьИзменения);
	
КонецПроцедуры

Процедура ОбновитьНовыеЗаписиНабораПоРазличнымНовымЗаписям(Знач Данные, Знач Отбор, ЕстьИзменения)
	
	// Получение количества записей для чтения.
	
	Если Отбор.Количество() = 0 Тогда
		ТекущиеНовыеЗаписи = Данные.НовыеЗаписи.Скопировать(); // ТаблицаЗначений
		КоличествоДляЧтения = Данные.КоличествоДляЧтения;
	Иначе
		ТекущиеНовыеЗаписи = Данные.НовыеЗаписи.Скопировать(Отбор); // ТаблицаЗначений
		
		КоличествоПоЗначениям = Данные.КоличествоПоЗначениям; // ТаблицаЗначений
		ИмяПоля = КоличествоПоЗначениям.Колонки[0].Имя;
		СтрокаКоличества = Данные.КоличествоПоЗначениям.Найти(Отбор[ИмяПоля], ИмяПоля);
		КоличествоДляЧтения = ?(СтрокаКоличества = Неопределено, 0, СтрокаКоличества.Количество);
	КонецЕсли;
	
	ОтборНовойЗаписи = Новый Структура("ВидИзмененияСтроки, " + Данные.ПоляСравнения, 1);
	ТекущиеНовыеЗаписи.Индексы.Добавить("ВидИзмененияСтроки, " + Данные.ПоляСравнения);

	КлючиЗаписей = ТекущиеНовыеЗаписи.Скопировать(, "ВидИзмененияСтроки, " + Данные.ПоляСравнения);
	КлючиЗаписей.Свернуть("ВидИзмененияСтроки, " + Данные.ПоляСравнения);
	КлючиЗаписей.Свернуть(Данные.ПоляСравнения, "ВидИзмененияСтроки");
	
	ОтборПоКлючуЗаписи = Новый Структура(Данные.ПоляСравнения);
	
	Если ОбновлятьНаборЗаписейЦеликом(КоличествоДляЧтения, КлючиЗаписей) Тогда
		
		ЗаблокироватьОбластьНабораЗаписей(Данные.НаборЗаписей, Данные.ПолноеИмяРегистра);
		Данные.НаборЗаписей.Прочитать();
		НовыеЗаписиНабора = Данные.НаборЗаписей.Выгрузить(); // ТаблицаЗначений
		НовыеЗаписиНабора.Индексы.Добавить(Данные.ПоляСравнения);
		
		Для каждого Строка Из КлючиЗаписей Цикл
			ЗаполнитьЗначенияСвойств(ОтборПоКлючуЗаписи, Строка);
			НайденныеСтроки = НовыеЗаписиНабора.НайтиСтроки(ОтборПоКлючуЗаписи);
			Если Строка.ВидИзмененияСтроки = -1 Тогда
				Если НайденныеСтроки.Количество() > 0 Тогда
					// Удаление старой строки.
					НовыеЗаписиНабора.Удалить(НайденныеСтроки[0]);
				КонецЕсли;
			Иначе
				// Добавление новой или обновление старой строки.
				Если НайденныеСтроки.Количество() = 0 Тогда
					ЗаполняемаяСтрока = НовыеЗаписиНабора.Добавить();
				Иначе
					ЗаполняемаяСтрока = НайденныеСтроки[0];
				КонецЕсли;
				ЗаполнитьЗначенияСвойств(ОтборНовойЗаписи, ОтборПоКлючуЗаписи);
				НайденныеЗаписи = ТекущиеНовыеЗаписи.НайтиСтроки(ОтборНовойЗаписи);
				Если НайденныеЗаписи.Количество() = 1 Тогда
					НоваяЗапись = НайденныеЗаписи[0];
				Иначе // Ошибка в параметре НовыеЗаписи.
					ИсключениеПриОшибкеПоискаЗаписи(Данные);
				КонецЕсли;
				ЗаполнитьЗначенияСвойств(ЗаполняемаяСтрока, НоваяЗапись);
			КонецЕсли;
		КонецЦикла;
		// Изменение набора записей, чтобы он отличался от новых записей набора.
		Если Данные.НаборЗаписей.Количество() = НовыеЗаписиНабора.Количество() Тогда
			Данные.НаборЗаписей.Добавить();
		КонецЕсли;
		
		ТекущиеДанные = Новый Структура("НаборЗаписей, ПоляСравнения,
			|ТолькоПроверка, ДополнительныеСвойства, ОбновлениеИБ");
		ЗаполнитьЗначенияСвойств(ТекущиеДанные, Данные);
		ТекущиеДанные.Вставить("НовыеЗаписи", НовыеЗаписиНабора);
		ТекущиеДанные.Вставить("НаборЗаписейПрочитан", Истина);
		
		ОбновитьНаборЗаписей(ТекущиеДанные, ЕстьИзменения);
	Иначе
		// Построчное обновление.
		УстановитьДополнительныеСвойства(Данные.НаборДляОднойЗаписи, Данные.ДополнительныеСвойства);
		Для каждого Строка Из КлючиЗаписей Цикл
			Данные.НаборДляОднойЗаписи.Очистить();
			ЗаполнитьЗначенияСвойств(ОтборПоКлючуЗаписи, Строка);
			Для каждого КлючИЗначение Из ОтборПоКлючуЗаписи Цикл
				УстановитьОтбор(
					Данные.НаборДляОднойЗаписи.Отбор[КлючИЗначение.Ключ], КлючИЗначение.Значение);
			КонецЦикла;
			ЗаблокироватьОбластьНабораЗаписей(Данные.НаборДляОднойЗаписи, Данные.ПолноеИмяРегистра);
			Если Строка.ВидИзмененияСтроки > -1 Тогда
				// Добавление новой или обновление существующей строки.
				ЗаполнитьЗначенияСвойств(ОтборНовойЗаписи, ОтборПоКлючуЗаписи);
				НайденныеЗаписи = ТекущиеНовыеЗаписи.НайтиСтроки(ОтборНовойЗаписи);
				Если НайденныеЗаписи.Количество() = 1 Тогда
					НоваяЗапись = НайденныеЗаписи[0];
				Иначе // Ошибка в параметре НовыеЗаписи.
					ИсключениеПриОшибкеПоискаЗаписи(Данные);
				КонецЕсли;
				ЗаполнитьЗначенияСвойств(Данные.НаборДляОднойЗаписи.Добавить(), НоваяЗапись);
			КонецЕсли;
			ЕстьИзменения = Истина;
			Если Данные.ТолькоПроверка Тогда
				Возврат;
			КонецЕсли;
			ЗаписатьОбъектИлиНаборЗаписей(Данные, Данные.НаборДляОднойЗаписи);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбновитьНовыеЗаписиНабораПоРазличнымНовымЗаписям.
Функция ОбновлятьНаборЗаписейЦеликом(КоличествоДляЧтения, КлючиЗаписей)
	
	Если КоличествоДляЧтения > 10000 Тогда
		Возврат Ложь; // Слишком большой набор записей.
	КонецЕсли;
	
	КоличествоУдаляемых = КлючиЗаписей.НайтиСтроки(
		Новый Структура("ВидИзмененияСтроки", -1)).Количество();
	
	КоличествоДобавляемых = КлючиЗаписей.НайтиСтроки(
		Новый Структура("ВидИзмененияСтроки", 1)).Количество();
	
	КоличествоДляЗаписи = КоличествоДляЧтения - КоличествоУдаляемых
		+ КоличествоДобавляемых;
	
	Если КоличествоДляЗаписи > 10000 Тогда
		Возврат Ложь; // Слишком большой набор записей.
	КонецЕсли;
	
	КоличествоИзменяемых = КлючиЗаписей.Количество()
		- (КоличествоУдаляемых + КоличествоДобавляемых);
	
	КоличествоНеизменных = КоличествоДляЧтения
		- (КоличествоУдаляемых + КоличествоИзменяемых);
	
	ЗатратыНаПерезаписьЦеликом =
	//                Операции:   |Чтение|Удаление|Вставка|
	      КоличествоУдаляемых   * ( 0.05  +  0.1          )
	    + КоличествоИзменяемых  * ( 0.05  +  0.1   +  1   )
	    + КоличествоДобавляемых * (                   1   )
	    + КоличествоНеизменных  * ( 0.05  +  0.1   +  1   );
	
	ЗатратыНаПерезаписьПоОднойЗаписи =
	//                Операции:   |Удаление|Вставка|
	      КоличествоУдаляемых   * (   0.5          )
	    + КоличествоИзменяемых  * (   0.5  +  1.2  )
	    + КоличествоДобавляемых * (   0.5  +  1.2  );
	
	Возврат ЗатратыНаПерезаписьЦеликом < ЗатратыНаПерезаписьПоОднойЗаписи;
	
КонецФункции

Процедура ИсключениеПриОшибкеПоискаЗаписи(Параметры)
	
	Для каждого СтрокаИзменений Из Параметры.НовыеЗаписи Цикл
		Если СтрокаИзменений.ВидИзмененияСтроки <>  1
		   И СтрокаИзменений.ВидИзмененияСтроки <> -1 Тогда
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ошибка в процедуре %1
				           |общего модуля %2.
				           |
				           |Неверное значение параметра %3 - колонка
				           |%4 содержит недопустимое значение ""%5"".
				           |
				           |Допустимо только 2 значения: ""1"" и ""-1"".'"),
				"ОбновитьНаборыЗаписей",
				"УправлениеДоступомСлужебный",
				"НовыеЗаписи",
				"ВидИзмененияСтроки",
				Строка(СтрокаИзменений.ВидИзмененияСтроки));
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
	КонецЦикла;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка в процедуре %1
		           |общего модуля %2.
		           |
		           |Не удалось найти требуемую в строку
		           |в значении параметра %3.'"),
		"ОбновитьНаборыЗаписей",
		"УправлениеДоступомСлужебный",
		"НовыеЗаписи");
	
	ВызватьИсключение ТекстОшибки;
	
КонецПроцедуры

Процедура ЗаблокироватьОбластьНабораЗаписей(НаборЗаписей, ПолноеИмяРегистра = Неопределено)
	
	Если НЕ ТранзакцияАктивна() Тогда
		Возврат;
	КонецЕсли;
	
	Если ПолноеИмяРегистра = Неопределено Тогда
		ПолноеИмяРегистра = Метаданные.НайтиПоТипу(ТипЗнч(НаборЗаписей)).ПолноеИмя();
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяРегистра);
	Для каждого ЭлементОтбора Из НаборЗаписей.Отбор Цикл
		Если ЭлементОтбора.Использование Тогда
			ЭлементБлокировки.УстановитьЗначение(ЭлементОтбора.ПутьКДанным, ЭлементОтбора.Значение);
		КонецЕсли;
	КонецЦикла;
	Блокировка.Заблокировать();
	
КонецПроцедуры

Процедура УстановитьОтбор(ЭлементОтбора, ЗначениеОтбора)
	
	ЭлементОтбора.Значение = ЗначениеОтбора;
	ЭлементОтбора.Использование = Истина;
	
КонецПроцедуры

Функция ЗаписьНесколькимиНаборами(Данные, Отбор, ИмяПоля, ЗначенияПоля)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ЗначенияПоля", ЗначенияПоля);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	КОЛИЧЕСТВО(*) КАК Количество
	|ИЗ
	|	&ТекущаяТаблица КАК ТекущаяТаблица
	|ГДЕ
	|	&УсловиеОтбора
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	КОЛИЧЕСТВО(*) КАК Количество
	|ИЗ
	|	&ТекущаяТаблица КАК ТекущаяТаблица
	|ГДЕ
	|	ТекущаяТаблица.ИмяПоля В(&ЗначенияПоля)
	|	И &УсловиеОтбора
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ТекущаяТаблица.ИмяПоля КАК ИмяПоля,
	|	КОЛИЧЕСТВО(*) КАК Количество
	|ИЗ
	|	&ТекущаяТаблица КАК ТекущаяТаблица
	|ГДЕ
	|	ТекущаяТаблица.ИмяПоля В(&ЗначенияПоля)
	|	И &УсловиеОтбора
	|
	|СГРУППИРОВАТЬ ПО
	|	ТекущаяТаблица.ИмяПоля";
	
	УсловиеОтбора = "ИСТИНА";
	Если Данные.ФиксированныйОтбор <> Неопределено Тогда
		Для каждого КлючИЗначение Из Данные.ФиксированныйОтбор Цикл
			УсловиеОтбора = УсловиеОтбора + "
			|	И ТекущаяТаблица." + КлючИЗначение.Ключ + " = &" + КлючИЗначение.Ключ; // @query-part-1
			Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		КонецЦикла;
	КонецЕсли;
	
	ОтборДобавляемых = Новый Структура;
	ОтборДобавляемых.Вставить("ВидИзмененияСтроки", 1);
	ОтборУдаляемых = Новый Структура;
	ОтборУдаляемых.Вставить("ВидИзмененияСтроки", -1);
	
	Для каждого КлючИЗначение Из Отбор Цикл
		УсловиеОтбора = УсловиеОтбора + "
		|	И ТекущаяТаблица." + КлючИЗначение.Ключ + " = &" + КлючИЗначение.Ключ; // @query-part-1
		Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		ОтборДобавляемых.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		ОтборУдаляемых.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "ИмяПоля", ИмяПоля);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ТекущаяТаблица", Данные.ПолноеИмяРегистра);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбора", УсловиеОтбора);
	
	РезультатыЗапросов = Запрос.ВыполнитьПакет();
	
	// Количество всех без отбора.
	КоличествоВсех = РезультатыЗапросов[0].Выгрузить()[0].Количество;
	Данные.Вставить("КоличествоДляЧтения", КоличествоВсех);
	
	// Количество обновляемых с отбором.
	КоличествоОбновляемых = РезультатыЗапросов[1].Выгрузить()[0].Количество;
	
	КоличествоДобавляемых = Данные.НовыеЗаписи.НайтиСтроки(ОтборДобавляемых).Количество();
	Если КоличествоДобавляемых > КоличествоОбновляемых Тогда
		КоличествоОбновляемых = КоличествоДобавляемых;
	КонецЕсли;
	
	КоличествоУдаляемых = Данные.НовыеЗаписи.НайтиСтроки(ОтборУдаляемых).Количество();
	Если КоличествоУдаляемых > КоличествоОбновляемых Тогда
		КоличествоОбновляемых = КоличествоУдаляемых;
	КонецЕсли;
	
	// Количество для чтения по значениям отбора.
	КоличествоПоЗначениям = РезультатыЗапросов[2].Выгрузить();
	КоличествоПоЗначениям.Индексы.Добавить(ИмяПоля);
	Данные.Вставить("КоличествоПоЗначениям", КоличествоПоЗначениям);
	
	Возврат КоличествоВсех * 0.7 > КоличествоОбновляемых;
	
КонецФункции

Процедура ПрочитатьКоличествоДляЧтения(Данные)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	КОЛИЧЕСТВО(*) КАК Количество
	|ИЗ
	|	&ТекущаяТаблица КАК ТекущаяТаблица
	|ГДЕ
	|	&УсловиеОтбора";
	
	УсловиеОтбора = "ИСТИНА";
	Если Данные.ФиксированныйОтбор <> Неопределено Тогда
		Для каждого КлючИЗначение Из Данные.ФиксированныйОтбор Цикл
			УсловиеОтбора = УсловиеОтбора + "
			|	И ТекущаяТаблица." + КлючИЗначение.Ключ + " = &" + КлючИЗначение.Ключ; // @query-part-1
			Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		КонецЦикла;
	КонецЕсли;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ТекущаяТаблица", Данные.ПолноеИмяРегистра);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловиеОтбора", УсловиеОтбора);
	
	Данные.Вставить("КоличествоДляЧтения", Запрос.Выполнить().Выгрузить()[0].Количество);
	
КонецПроцедуры

Процедура УстановитьДополнительныеСвойства(НаборЗаписей, ДополнительныеСвойства)
	
	Если ТипЗнч(ДополнительныеСвойства) = Тип("Структура") Тогда
		Для каждого КлючИЗначение Из ДополнительныеСвойства Цикл
			НаборЗаписей.ДополнительныеСвойства.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбновитьРегистрСведений.

Функция ЗначенияКолонкиТаблицы(Таблица, ИмяКолонки)
	
	НоваяТаблица = Таблица.Скопировать(, ИмяКолонки);
	
	НоваяТаблица.Свернуть(ИмяКолонки);
	
	Возврат НоваяТаблица.ВыгрузитьКолонку(ИмяКолонки);
	
КонецФункции

// Обслуживание таблиц ВидыДоступа и ЗначенияДоступа в формах редактирования.

Процедура ДобавитьРеквизитыВспомогательныхДанныхВФорму(Форма, ИмяРеквизитаХранилищаТаблиц)
	
	ДобавляемыеРеквизиты = Новый Массив;
	ОписаниеТиповЗначенийДоступа = Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип;
	
	ПутьКОбъекту = ?(ЗначениеЗаполнено(ИмяРеквизитаХранилищаТаблиц), ИмяРеквизитаХранилищаТаблиц + ".", "");
	
	// Добавление реквизитов в таблицу ВидыДоступа.
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"Используется", Новый ОписаниеТипов("Булево"), ПутьКОбъекту + "ВидыДоступа"));
	
	// Добавление отдельных реквизитов.
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ТекущийВидДоступа", ОписаниеТиповЗначенийДоступа));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ТекущиеТипыВыбираемыхЗначений", Новый ОписаниеТипов("СписокЗначений")));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ТекущийТипВыбираемыхЗначений", ОписаниеТиповЗначенийДоступа));
	
	Если НЕ РеквизитФормыСуществует(Форма, "ИспользоватьВнешнихПользователей") Тогда
		ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
			"ИспользоватьВнешнихПользователей", Новый ОписаниеТипов("Булево")));
	КонецЕсли;
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ИмяРеквизитаХранилищаТаблиц", Новый ОписаниеТипов("Строка")));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ЭтоПрофильГруппДоступа", Новый ОписаниеТипов("Булево")));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ВидДоступаПользователи", ОписаниеТиповЗначенийДоступа));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ВидДоступаВнешниеПользователи", ОписаниеТиповЗначенийДоступа));
	
	// Добавление таблицы ВсеВидыДоступа.
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ВсеВидыДоступа", Новый ОписаниеТипов("ТаблицаЗначений")));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"Ссылка", ОписаниеТиповЗначенийДоступа, "ВсеВидыДоступа"));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"Представление", Новый ОписаниеТипов("Строка"), "ВсеВидыДоступа"));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"Используется", Новый ОписаниеТипов("Булево"), "ВсеВидыДоступа"));
	
	// Добавление таблицы ПредставленияВсеРазрешены.
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ПредставленияВсеРазрешены", Новый ОписаниеТипов("ТаблицаЗначений")));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"Имя", Новый ОписаниеТипов("Строка"), "ПредставленияВсеРазрешены"));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"Представление", Новый ОписаниеТипов("Строка"), "ПредставленияВсеРазрешены"));
	
	// Добавление таблицы ВсеТипыВыбираемыхЗначений.
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ВсеТипыВыбираемыхЗначений", Новый ОписаниеТипов("ТаблицаЗначений")));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ВидДоступа", ОписаниеТиповЗначенийДоступа, "ВсеТипыВыбираемыхЗначений"));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ТипЗначений", ОписаниеТиповЗначенийДоступа, "ВсеТипыВыбираемыхЗначений"));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ПредставлениеТипа", Новый ОписаниеТипов("Строка"), "ВсеТипыВыбираемыхЗначений"));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ИмяТаблицы", Новый ОписаниеТипов("Строка"), "ВсеТипыВыбираемыхЗначений"));
	
	ДобавляемыеРеквизиты.Добавить(Новый РеквизитФормы(
		"ИерархияЭлементов", Новый ОписаниеТипов("Булево"), "ВсеТипыВыбираемыхЗначений"));
	
	Форма.ИзменитьРеквизиты(ДобавляемыеРеквизиты);
	
КонецПроцедуры

Процедура ЗаполнитьТаблицуВсеВидыДоступаВФорме(Форма)
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	ВсеВидыДоступа = Форма.ВсеВидыДоступа;
	ИспользуемыеВидыДоступа = ИспользуемыеВидыДоступа();
	
	Для Каждого СвойстваВидаДоступа Из СвойстваВидовДоступа.Массив Цикл
		Строка = ВсеВидыДоступа.Добавить();
		Строка.Ссылка        = СвойстваВидаДоступа.Ссылка;
		Строка.Используется  = ИспользуемыеВидыДоступа.Получить(Строка.Ссылка) <> Неопределено;
		// Обеспечение уникальности представлений.
		Представление = ПредставлениеВидаДоступа(СвойстваВидаДоступа);
		Отбор = Новый Структура("Представление", Представление);
		Пока ВсеВидыДоступа.НайтиСтроки(Отбор).Количество() > 0 Цикл
			Отбор.Представление = Отбор.Представление + " ";
		КонецЦикла;
		Строка.Представление = Отбор.Представление;
	КонецЦикла;
	
КонецПроцедуры

Процедура ЗаполнитьТаблицуПредставленияВсеРазрешеныВФорме(Форма, ЭтоПрофиль)
	
	ПредставленияВсеРазрешены = Форма.ПредставленияВсеРазрешены;
	
	Если ЭтоПрофиль Тогда
		Строка = ПредставленияВсеРазрешены.Добавить();
		Строка.Имя = "ВначалеВсеЗапрещены";
		Строка.Представление = НСтр("ru = 'Все запрещены, исключения назначаются в группах доступа'");
		
		Строка = ПредставленияВсеРазрешены.Добавить();
		Строка.Имя = "ВначалеВсеРазрешены";
		Строка.Представление = НСтр("ru = 'Все разрешены, исключения назначаются в группах доступа'");
		
		Строка = ПредставленияВсеРазрешены.Добавить();
		Строка.Имя = "ВсеЗапрещены";
		Строка.Представление = НСтр("ru = 'Все запрещены, исключения назначаются в профиле'");
		
		Строка = ПредставленияВсеРазрешены.Добавить();
		Строка.Имя = "ВсеРазрешены";
		Строка.Представление = НСтр("ru = 'Все разрешены, исключения назначаются в профиле'");
	Иначе
		Строка = ПредставленияВсеРазрешены.Добавить();
		Строка.Имя = "ВсеЗапрещены";
		Строка.Представление = НСтр("ru = 'Все запрещены'");
		
		Строка = ПредставленияВсеРазрешены.Добавить();
		Строка.Имя = "ВсеРазрешены";
		Строка.Представление = НСтр("ru = 'Все разрешены'");
	КонецЕсли;
	
	СписокВыбора = Форма.Элементы.ВидыДоступаВсеРазрешеныПредставление.СписокВыбора; // СписокЗначений
	
	Для каждого Строка Из ПредставленияВсеРазрешены Цикл
		СписокВыбора.Добавить(Строка.Представление);
	КонецЦикла;
	
КонецПроцедуры

Процедура ОформитьТаблицуВидыДоступаВФорме(Форма)
	
	Параметры = ПараметрыФормыРедактированияРазрешенныхЗначений(Форма);
	
	// Оформление отображения неиспользуемых видов доступа.
	ЭлементУсловногоОформления = Форма.УсловноеОформление.Элементы.Добавить();
	
	ЭлементЦветаОформления = ЭлементУсловногоОформления.Оформление.Элементы.Найти("TextColor");
	ЭлементЦветаОформления.Значение = WebЦвета.Серый;
	ЭлементЦветаОформления.Использование = Истина;
	
	ГруппаЭлементовОтбораДанных = ЭлементУсловногоОформления.Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
	ГруппаЭлементовОтбораДанных.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
	ГруппаЭлементовОтбораДанных.Использование = Истина;
	
	ЭлементОтбораДанных = ГруппаЭлементовОтбораДанных.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ЭлементОтбораДанных.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных(Параметры.ПутьКТаблицам + "ВидыДоступа.ВидДоступа");
	ЭлементОтбораДанных.ВидСравнения   = ВидСравненияКомпоновкиДанных.НеРавно;
	ЭлементОтбораДанных.ПравоеЗначение = Неопределено;
	ЭлементОтбораДанных.Использование  = Истина;
	
	ЭлементОтбораДанных = ГруппаЭлементовОтбораДанных.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
	ЭлементОтбораДанных.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных(Параметры.ПутьКТаблицам + "ВидыДоступа.Используется");
	ЭлементОтбораДанных.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
	ЭлементОтбораДанных.ПравоеЗначение = Ложь;
	ЭлементОтбораДанных.Использование  = Истина;
	
	ЭлементОформляемогоПоля = ЭлементУсловногоОформления.Поля.Элементы.Добавить();
	ЭлементОформляемогоПоля.Поле = Новый ПолеКомпоновкиДанных("ВидыДоступа");
	ЭлементОформляемогоПоля.Использование = Истина;
	
КонецПроцедуры

Процедура ОформитьТаблицуЗначенияДоступаВФорме(Форма)
	
	Параметры = ПараметрыФормыРедактированияРазрешенныхЗначений(Форма);
	ПустыеСсылкиЗначенийДоступа = ПустыеСсылкиЗначенийДоступа();
	
	// Оформление отображения пустых ссылок значений доступа.
	Для Каждого Строка Из ПустыеСсылкиЗначенийДоступа Цикл
		ЭлементУсловногоОформления = Форма.УсловноеОформление.Элементы.Добавить();
		
		ЭлементОформленияТекст = ЭлементУсловногоОформления.Оформление.Элементы.Найти("Text");
		ЭлементОформленияТекст.Значение = Строка.Представление;
		ЭлементОформленияТекст.Использование = Истина;
		
		ЭлементОтбораДанных = ЭлементУсловногоОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
		ЭлементОтбораДанных.ЛевоеЗначение  = Новый ПолеКомпоновкиДанных(Параметры.ПутьКТаблицам + "ЗначенияДоступа.ЗначениеДоступа");
		ЭлементОтбораДанных.ВидСравнения   = ВидСравненияКомпоновкиДанных.Равно;
		ЭлементОтбораДанных.ПравоеЗначение = Строка.ПустаяСсылка;
		ЭлементОтбораДанных.Использование  = Истина;
		
		ЭлементОформляемогоПоля = ЭлементУсловногоОформления.Поля.Элементы.Добавить();
		ЭлементОформляемогоПоля.Поле = Новый ПолеКомпоновкиДанных("ЗначенияДоступаЗначениеДоступа");
		ЭлементОформляемогоПоля.Использование = Истина;
	КонецЦикла;
	
КонецПроцедуры

Процедура УдалитьЛишниеЗначенияДоступа(Форма, ТекущийОбъект = Неопределено)
	
	Параметры = ПараметрыФормыРедактированияРазрешенныхЗначений(Форма, ТекущийОбъект);
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	ПоТипамГруппИЗначений = СвойстваВидовДоступа.ПоТипамГруппИЗначений;
	
	Отбор = УправлениеДоступомСлужебныйКлиентСервер.ОтборВТаблицахФормыРедактированияРазрешенныхЗначений(
		Форма, "");
	
	Индекс = Параметры.ЗначенияДоступа.Количество()-1;
	Пока Индекс >= 0 Цикл
		ЗначениеДоступа = Параметры.ЗначенияДоступа[Индекс].ЗначениеДоступа;
		
		СвойстваВидаДоступа = ПоТипамГруппИЗначений.Получить(ТипЗнч(ЗначениеДоступа)); // См. СвойстваВидаДоступа
		Если СвойстваВидаДоступа <> Неопределено Тогда
			ЗаполнитьЗначенияСвойств(Отбор, Параметры.ЗначенияДоступа[Индекс]);
			Отбор.Вставить("ВидДоступа", СвойстваВидаДоступа.Ссылка);
		КонецЕсли;
		
		Если СвойстваВидаДоступа = Неопределено
		 ИЛИ Параметры.ЗначенияДоступа[Индекс].ВидДоступа <> Отбор.ВидДоступа
		 ИЛИ Параметры.ВидыДоступа.НайтиСтроки(Отбор).Количество() = 0 Тогда
			
			Параметры.ЗначенияДоступа.Удалить(Индекс);
		КонецЕсли;
		Индекс = Индекс - 1;
	КонецЦикла;
	
КонецПроцедуры

Процедура УдалитьНесуществующиеВидыИЗначенияДоступа(Форма, ТекущийОбъект = Неопределено)
	
	Параметры = ПараметрыФормыРедактированияРазрешенныхЗначений(Форма, ТекущийОбъект);
	
	Индекс = Параметры.ВидыДоступа.Количество()-1;
	Пока Индекс >= 0 Цикл
		ВидДоступа = Параметры.ВидыДоступа[Индекс].ВидДоступа;
		Если СвойстваВидаДоступа(ВидДоступа) = Неопределено Тогда
			Параметры.ВидыДоступа.Удалить(Индекс);
		КонецЕсли;
		Индекс = Индекс - 1;
	КонецЦикла;
	
	УдалитьЛишниеЗначенияДоступа(Форма, ТекущийОбъект);
	
КонецПроцедуры

Функция ПараметрыФормыРедактированияРазрешенныхЗначений(Форма, ТекущийОбъект = Неопределено)
	
	Возврат УправлениеДоступомСлужебныйКлиентСервер.ПараметрыФормыРедактированияРазрешенныхЗначений(
		Форма, ТекущийОбъект);
	
КонецФункции

Функция РеквизитФормыСуществует(Форма, ИмяРеквизита)
	
	Структура = Новый Структура(ИмяРеквизита, Null);
	
	ЗаполнитьЗначенияСвойств(Структура, Форма);
	
	Возврат Структура[ИмяРеквизита] <> Null;
	
КонецФункции

Функция ПустыеСсылкиЗначенийДоступа() Экспорт
	
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("ПустаяСсылка", Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	Таблица.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка",
		,,, Новый КвалификаторыСтроки(150, ДопустимаяДлина.Переменная)));
	
	Для Каждого Тип Из Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип.Типы() Цикл
		Типы = Новый Массив;
		Типы.Добавить(Тип);
		ОписаниеТипа = Новый ОписаниеТипов(Типы);
		НоваяСтрока = Таблица.Добавить();
		НоваяСтрока.ПустаяСсылка = ОписаниеТипа.ПривестиЗначение(Неопределено);
		НоваяСтрока.Представление = "<" + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Пустая ссылка %1'"), Строка(Тип)) + ">";
	КонецЦикла;
	
	Возврат Таблица;
	
КонецФункции

// Для процедуры УстановкаПараметровСеанса.

Функция ВсеКомбинацииВидовДоступа(НеупорядоченныйМассивИмен)
	
	// Ограничение на максимальную длину комбинации, чтобы не допустить
	// перегрузку параметров сеанса и препроцессора шаблонов ОДД.
	МаксимальнаяДлинаКомбинации = 4;
	
	Список = Новый СписокЗначений;
	Если ТипЗнч(НеупорядоченныйМассивИмен) = Тип("ФиксированныйМассив") Тогда
		Список.ЗагрузитьЗначения(Новый Массив(НеупорядоченныйМассивИмен));
	Иначе
		Список.ЗагрузитьЗначения(НеупорядоченныйМассивИмен);
	КонецЕсли;
	Список.СортироватьПоЗначению();
	МассивИмен = Список.ВыгрузитьЗначения();
	
	ИтогСтроки = Новый Массив;
	ИтогСтрока = Новый Массив;
	
	// Полный список поддерживается всегда.
	Для каждого Имя Из МассивИмен Цикл
		ИтогСтрока.Добавить(Имя);
	КонецЦикла;
	
	ИтогСтроки.Добавить(ИтогСтрока);
	
	Если МассивИмен.Количество() < 3 Тогда
		Возврат ГруппыСтрокВСтроку(ИтогСтроки);
	КонецЕсли;
	
	ПервоеИмя = МассивИмен[0];
	МассивИмен.Удалить(0);
	
	ПоследнееИмя = МассивИмен[МассивИмен.Количество()-1];
	МассивИмен.Удалить(МассивИмен.Количество()-1);
	
	КоличествоИменВКомбинации = МассивИмен.Количество();
	
	Если КоличествоИменВКомбинации > 1 Тогда
		
		Если (КоличествоИменВКомбинации-1) <= МаксимальнаяДлинаКомбинации Тогда
			ДлинаКомбинации = КоличествоИменВКомбинации-1;
		Иначе
			ДлинаКомбинации = МаксимальнаяДлинаКомбинации;
		КонецЕсли;
		
		ПозицииИменВКомбинации = Новый Массив;
		Для Счетчик = 1 По ДлинаКомбинации Цикл
			ПозицииИменВКомбинации.Добавить(Счетчик);
		КонецЦикла;
		
		Пока ДлинаКомбинации > 0 Цикл
			Пока Истина Цикл
				// Добавление комбинации из текущих позиций.
				ИтогСтрока = Новый Массив;
				ИтогСтрока.Добавить(ПервоеИмя);
				Для Индекс = 0 По ДлинаКомбинации-1 Цикл
					ИтогСтрока.Добавить(МассивИмен[ПозицииИменВКомбинации[Индекс]-1]);
				КонецЦикла;
				ИтогСтрока.Добавить(ПоследнееИмя);
				ИтогСтроки.Добавить(ИтогСтрока);
				// Продвижение позиции в комбинации.
				Индекс = ДлинаКомбинации-1;
				Пока Индекс >= 0 Цикл
					Если ПозицииИменВКомбинации[Индекс] < КоличествоИменВКомбинации - (ДлинаКомбинации - (Индекс+1)) Тогда
						ПозицииИменВКомбинации[Индекс] = ПозицииИменВКомбинации[Индекс] + 1;
						// Заполнение старших позиций начальными значениями.
						Для ИндексСтаршейПозиции = Индекс+1 По ДлинаКомбинации-1 Цикл
							ПозицииИменВКомбинации[ИндексСтаршейПозиции] =
								ПозицииИменВКомбинации[Индекс] + ИндексСтаршейПозиции - Индекс;
						КонецЦикла;
						Прервать;
					Иначе
						Индекс = Индекс - 1;
					КонецЕсли;
				КонецЦикла;
				Если Индекс < 0 Тогда
					Прервать;
				КонецЕсли;
			КонецЦикла;
			ДлинаКомбинации = ДлинаКомбинации - 1;
			Для Индекс = 0 По ДлинаКомбинации - 1 Цикл
				ПозицииИменВКомбинации[Индекс] = Индекс + 1;
			КонецЦикла;
		КонецЦикла;
	КонецЕсли;
	
	ИтогСтрока = Новый Массив;
	ИтогСтрока.Добавить(ПервоеИмя);
	ИтогСтрока.Добавить(ПоследнееИмя);
	ИтогСтроки.Добавить(ИтогСтрока);
	
	Возврат ГруппыСтрокВСтроку(ИтогСтроки);
	
КонецФункции

Функция ГруппыСтрокВСтроку(ГруппыСтрок)
	
	ИтогСтроки = Новый Массив;
	
	Для Каждого ИтогСтрока Из ГруппыСтрок Цикл
		ИтогСтроки.Добавить(СтрСоединить(ИтогСтрока, ","));
	КонецЦикла;
	
	Строки = СтрСоединить(
		ИтогСтроки,
		",
		|,");
	
	Шаблон =
		"%2%1%2
		|";
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Шаблон, Строки, ",");
	
КонецФункции

// Для процедур ОбновитьНаборыЗначенийДоступа, ПриИзмененииНаборовЗначенийДоступа.

// Проверяет, что наборы в табличной части отличаются от новых наборов.
Функция НаборыЗначенийДоступаТабличнойЧастиИзменены(СсылкаНаОбъект, НовыеНаборы)
	
	СтарыеНаборы = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
		СсылкаНаОбъект, "НаборыЗначенийДоступа").Выгрузить();
	
	Если СтарыеНаборы.Количество() <> НовыеНаборы.Количество() Тогда
		Возврат Истина;
	КонецЕсли;
	
	СтарыеНаборы.Колонки.Добавить("ВидДоступа", Новый ОписаниеТипов("Строка"));
	УправлениеДоступом.ДобавитьНаборыЗначенийДоступа(
		СтарыеНаборы, УправлениеДоступом.ТаблицаНаборыЗначенийДоступа(), Ложь, Истина);
	
	ПоляПоиска = "НомерНабора, ЗначениеДоступа, Уточнение, Чтение, Изменение";
	
	НовыеНаборы.Индексы.Добавить(ПоляПоиска);
	Отбор = Новый Структура(ПоляПоиска);
	
	Для каждого Строка Из СтарыеНаборы Цикл
		ЗаполнитьЗначенияСвойств(Отбор, Строка);
		Если НовыеНаборы.НайтиСтроки(Отбор).Количество() <> 1 Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для функции РазрешенныеЗначенияДляДинамическогоСписка.
Процедура ДобавитьЗапросВПакет(ТекстПакета, ТекстЗапроса)
	
	Разделитель =
	"
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|";
	
	ТекстПакета = ТекстПакета + Разделитель + ТекстЗапроса;
	
КонецПроцедуры

// Для функции РазрешенныеЗначенияДляДинамическогоСписка.
Процедура ОбъединитьЗапросСЗапросом(ТекстЗапроса, ДобавляемыйТекстЗапроса)
	
	Объединитель =
	"
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|"; // @query-part-1
	
	ТекстЗапроса = ТекстЗапроса + Объединитель + ДобавляемыйТекстЗапроса;
	
КонецПроцедуры

// Обновление свойств видов доступа.

// Для функций ПроверенныеСвойстваВидовДоступаСеанса, ПредставлениеВидовДоступа.
// Смотри также УправлениеДоступомПереопределяемый.ПриЗаполненииВидовДоступа.
//
// Возвращаемое значение:
//   ТаблицаЗначений:
//     * Имя - Строка
//     * Представление - Строка
//     * ТипЗначений - Тип
//     * ТипГруппЗначений - Тип
//     * НесколькоГруппЗначений - Булево
//     * ДополнительныеТипы - см. НоваяТаблицаДополнительныеТипыВидаДоступа
// 
Функция ЗаполненныеВидыДоступаСеанса()
	
	// 1. Заполнение данных, указанных при внедрении.
	
	ВидыДоступа = Новый ТаблицаЗначений;
	ВидыДоступа.Колонки.Добавить("Имя",                    Новый ОписаниеТипов("Строка"));
	ВидыДоступа.Колонки.Добавить("Представление",          Новый ОписаниеТипов("Строка"));
	ВидыДоступа.Колонки.Добавить("ТипЗначений",            Новый ОписаниеТипов("Тип"));
	ВидыДоступа.Колонки.Добавить("ТипГруппЗначений",       Новый ОписаниеТипов("Тип"));
	ВидыДоступа.Колонки.Добавить("НесколькоГруппЗначений", Новый ОписаниеТипов("Булево"));
	ВидыДоступа.Колонки.Добавить("ДополнительныеТипы",     Новый ОписаниеТипов("ТаблицаЗначений"));
	
	ИнтеграцияПодсистемБСП.ПриЗаполненииВидовДоступа(ВидыДоступа);
	УправлениеДоступомПереопределяемый.ПриЗаполненииВидовДоступа(ВидыДоступа);
	
	Возврат ВидыДоступа;
	
КонецФункции

// Для функции ЗаполненныеВидыДоступаСеанса и
// процедуры ДобавитьДополнительныеТипыВидаДоступа общего модуля УправлениеДоступом.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * ТипЗначений - Тип
//    * ТипГруппЗначений - Тип
//    * НесколькоГруппЗначений - Булево
//
Функция НоваяТаблицаДополнительныеТипыВидаДоступа() Экспорт
	
	ДополнительныеТипы = Новый ТаблицаЗначений;
	ДополнительныеТипы.Колонки.Добавить("ТипЗначений",            Новый ОписаниеТипов("Тип"));
	ДополнительныеТипы.Колонки.Добавить("ТипГруппЗначений",       Новый ОписаниеТипов("Тип"));
	ДополнительныеТипы.Колонки.Добавить("НесколькоГруппЗначений", Новый ОписаниеТипов("Булево"));
	
	Возврат ДополнительныеТипы;
	
КонецФункции

// Для процедур ПриЗаполненииВсехПараметровРаботыРасширений и
// ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации.
//
Процедура ОбновитьГруппыИНаборыЗначенийДоступаПриИзмененииТиповГруппИЗначений()
	
	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса();
	НовоеЗначение = Кэш.ХешСуммы;
	
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ТипыГруппИЗначенийДоступа";
	СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
	СтароеЗначение = НовыеХешСуммыСвойствВидовДоступа(СтароеЗначение);
	
	Если СтароеЗначение.ХешСуммаТиповГруппИЗначенийДоступа
	    = НовоеЗначение.ХешСуммаТиповГруппИЗначенийДоступа Тогда
		Возврат;
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
	ЭлементБлокировки.УстановитьЗначение("ВерсияРасширений", Справочники.ВерсииРасширений.ПустаяСсылка());
	ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		УжеИзменен = Ложь;
		СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
		СтароеЗначение = НовыеХешСуммыСвойствВидовДоступа(СтароеЗначение);
		Если СтароеЗначение.ХешСуммаТиповГруппИЗначенийДоступа
		    <> НовоеЗначение.ХешСуммаТиповГруппИЗначенийДоступа Тогда
			Если УжеИзменен Тогда
				ПроверитьАктуальностьМетаданных();
			КонецЕсли;
			РегистрыСведений.ГруппыЗначенийДоступа.ОбновитьВспомогательныеДанныеРегистраПоИзменениямКонфигурации();
			РегистрыСведений.НаборыЗначенийДоступа.ОбновитьВспомогательныеДанныеРегистраПоИзменениямКонфигурации();
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, НовоеЗначение, Истина);
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Смотри УправлениеДоступомПереопределяемый.ПриЗаполненииВидовДоступа.
// Смотри также заполнение в функции ПроверенныеСвойстваВидовДоступаСеанса.
//
// Возвращаемое значение:
//   ФиксированнаяСтруктура:
//     * Массив - ФиксированныйМассив из см. СвойстваВидаДоступа
//     * ПоИменам - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Строка
//         ** Значение - см. СвойстваВидаДоступа
//     * ПоСсылкам - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - СправочникСсылка
//         ** Значение - см. СвойстваВидаДоступа
//     * ПоТипамЗначений - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - см. СвойстваВидаДоступа
//     * ПоТипамГруппИЗначений - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - см. СвойстваВидаДоступа
//     * ПоТипамЗначенийСИерархией - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - см. СвойстваВидаДоступа
//     * ЗначенияДоступаСГруппами - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - см. ЗначенияДоступаСГруппами
//     * БезГруппДляЗначенияДоступа - ФиксированныйМассив из Строка
//     * СОднойГруппойДляЗначенияДоступа - ФиксированныйМассив из Строка
//     * ТипыЗначенийДоступаСГруппами - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - СправочникСсылка
//
Функция СвойстваВидовДоступа() Экспорт
	
	Кэш = УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса();
	
	ТекущаяДатаСеанса = ТекущаяДатаСеанса();
	Если Кэш.Проверка.Дата + 3 > ТекущаяДатаСеанса Тогда
		Возврат Кэш.СвойстваСеанса;
	КонецЕсли;
	
	НовоеЗначение = Кэш.ХешСуммы;
	
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.СвойстваВидовДоступа";
	СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
	СтароеЗначение = НовыеХешСуммыСвойствВидовДоступа(СтароеЗначение);
	
	Если СтароеЗначение.ХешСумма <> НовоеЗначение.ХешСумма Тогда
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
		ЭлементБлокировки.УстановитьЗначение("ВерсияРасширений", Справочники.ВерсииРасширений.ПустаяСсылка());
		ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			УжеИзменен = Ложь;
			СтароеЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина, УжеИзменен);
			СтароеЗначение = НовыеХешСуммыСвойствВидовДоступа(СтароеЗначение);
			Если СтароеЗначение.ХешСумма <> НовоеЗначение.ХешСумма Тогда
				Если УжеИзменен Тогда
					ПроверитьАктуальностьМетаданных();
				КонецЕсли;
				УстановитьОтключениеБезопасногоРежима(Истина);
				УстановитьПривилегированныйРежим(Истина);
				СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, НовоеЗначение, Истина);
				УстановитьПривилегированныйРежим(Ложь);
				УстановитьОтключениеБезопасногоРежима(Ложь);
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЕсли;
	
	Кэш.Проверка.Дата = ТекущаяДатаСеанса;
	
	Возврат Кэш.СвойстваСеанса;
	
КонецФункции

// Для процедуры УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса.
// Возвращает свойства видов доступа, заполняемых при внедрении в процедуре ПриЗаполненииВидовДоступа
// общего модуля УправлениеДоступомПереопределяемый и одноименных обработчиках этого события.
//
// Смотри также УправлениеДоступомПереопределяемый.ПриЗаполненииВидовДоступа.
//
// Параметры:
//  ХешСуммы - см. НовыеХешСуммыСвойствВидовДоступа
//
// Возвращаемое значение:
//   см. СвойстваВидовДоступа
//
Функция ПроверенныеСвойстваВидовДоступаСеанса(ХешСуммы) Экспорт
	
	ВидыДоступа = ЗаполненныеВидыДоступаСеанса();
	
	// 1. Проверки:
	// - тип значений доступа не указан для 2-х видов доступа
	// - тип значений доступа Пользователи, ГруппыПользователей используется только для вида доступа Пользователи.
	// Т тип значений доступа ВнешниеПользователи, ГруппыВнешнихПользователей используется только для вида доступа
	// ВнешниеПользователи.
	// И имена видов доступа Объект, Условие, НастройкиПрав, ПравоЧтения, ПравоИзменения не указаны.
	// Т тип групп значений не совпадает с типом значений.
	//
	// 2. Подготовка различных коллекций свойств видов доступа, используемых при работе программы.
	
	МассивСвойств         = Новый Массив; // Массив из см. СвойстваВидаДоступа
	ПоСсылкам             = Новый Соответствие;
	ПоИменам              = Новый Соответствие;
	ПоТипамЗначений       = Новый Соответствие;
	ПоТипамГруппИЗначений = Новый Соответствие;
	
	ЗначенияДоступаСГруппами = ЗначенияДоступаСГруппами();
	
	Параметры = Новый Структура;
	Параметры.Вставить("ОпределяемыеТипыЗначенийДоступа",
		УправлениеДоступомСлужебныйПовтИсп.ТипыПоляТаблицы("ОпределяемыйТип.ЗначениеДоступа"));
	
	ЗаголовокОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ошибка в процедуре %1 общего модуля %2.'"),
		"ПриЗаполненииВидовДоступа", "УправлениеДоступомПереопределяемый")
		+ Символы.ПС
		+ Символы.ПС;
	
	Параметры.Вставить("ЗаголовокОшибки", ЗаголовокОшибки);
	
	ВсеИменаВидовДоступа = Новый Соответствие;
	ВсеИменаВидовДоступа.Вставить(ВРег("Объект"),         Истина);
	ВсеИменаВидовДоступа.Вставить(ВРег("Условие"),        Истина);
	ВсеИменаВидовДоступа.Вставить(ВРег("НастройкиПрав"),  Истина);
	ВсеИменаВидовДоступа.Вставить(ВРег("ПравоЧтения"),    Истина);
	ВсеИменаВидовДоступа.Вставить(ВРег("ПравоИзменения"), Истина);
	
	ВсеТипыЗначений      = Новый Соответствие;
	ВсеТипыГруппЗначений = Новый Соответствие;
	ВсеСвойства          = Новый Соответствие;
	
	ОписаниеВерсии = Новый Структура("СвойстваВерсии", Новый Массив);
	ДобавитьЭлементВерсии(ОписаниеВерсии, "Версия", "1");
	
	Для Каждого ВидДоступа Из ВидыДоступа Цикл
		Если ВсеИменаВидовДоступа[ВРег(ВидДоступа.Имя)] <> Неопределено Тогда
			ТекстОшибки = ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Имя вида доступа ""%1"" уже определено.'"),
				ВидДоступа.Имя);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		// Проверка повторения типов значений и групп.
		ПроверитьТип(ВидДоступа, ВидДоступа.ТипЗначений,      ВсеТипыЗначений,      Параметры);
		ПроверитьТип(ВидДоступа, ВидДоступа.ТипГруппЗначений, ВсеТипыГруппЗначений, Параметры, Истина);
		// Проверка пересечения типов значений и групп.
		ПроверитьТип(ВидДоступа, ВидДоступа.ТипЗначений,      ВсеТипыГруппЗначений, Параметры,       , Истина);
		ПроверитьТип(ВидДоступа, ВидДоступа.ТипГруппЗначений, ВсеТипыЗначений,      Параметры, Истина, Истина);
		
		ТаблицаДополнительныеТипы = ВидДоступа.ДополнительныеТипы; // См. НоваяТаблицаДополнительныеТипыВидаДоступа
		
		Для Каждого Строка Из ТаблицаДополнительныеТипы Цикл
			// Проверка повторения типов значений и групп.
			ПроверитьТип(ВидДоступа, Строка.ТипЗначений,      ВсеТипыЗначений,      Параметры);
			ПроверитьТип(ВидДоступа, Строка.ТипГруппЗначений, ВсеТипыГруппЗначений, Параметры, Истина);
			// Проверка пересечения типов значений и групп.
			ПроверитьТип(ВидДоступа, Строка.ТипЗначений,      ВсеТипыГруппЗначений, Параметры,       , Истина);
			ПроверитьТип(ВидДоступа, Строка.ТипГруппЗначений, ВсеТипыЗначений,      Параметры, Истина, Истина);
		КонецЦикла;
		
		ПустаяСсылкаТипаЗначений = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(
			Метаданные.НайтиПоТипу(ВидДоступа.ТипЗначений).ПолноеИмя()).ПустаяСсылка();
		
		Свойства = НовыеСвойстваВидаДоступа(ВидДоступа, ПустаяСсылкаТипаЗначений);
		ОписаниеВерсии.СвойстваВерсии.Добавить("");
		ДобавитьСвойстваВерсии(ОписаниеВерсии, Свойства,
			"Имя, ТипЗначений, ТипГруппЗначений, НесколькоГруппЗначений");
		
		ТипыВыбираемыхЗначений = Новый Массив;
		ЗаполнитьЗначенияДоступаСГруппами(Свойства, ЗначенияДоступаСГруппами, Свойства, ТипыВыбираемыхЗначений);
		ДополнительныеТипы = Новый Массив;
		Для Каждого Строка Из ТаблицаДополнительныеТипы Цикл
			ДополнительныйТип = ДополнительныйТипВидаДоступа(Строка);
			ДополнительныеТипы.Добавить(ДополнительныйТип);
			ДобавитьСвойстваВерсии(ОписаниеВерсии, Свойства,
				"ТипЗначений, ТипГруппЗначений, НесколькоГруппЗначений");
			ЗаполнитьЗначенияДоступаСГруппами(Строка, ЗначенияДоступаСГруппами, Свойства, ТипыВыбираемыхЗначений);
		КонецЦикла;
		
		Свойства.ТипыВыбираемыхЗначений = Новый ФиксированныйМассив(ТипыВыбираемыхЗначений);
		Свойства.ДополнительныеТипы     = Новый ФиксированныйМассив(ДополнительныеТипы);
		ВсеСвойства.Вставить(Свойства, Новый ФиксированнаяСтруктура(Свойства));
		Свойства = ВсеСвойства.Получить(Свойства);
		
		МассивСвойств.Добавить(Свойства);
		ПоИменам.Вставить(Свойства.Имя, Свойства);
		ПоСсылкам.Вставить(ПустаяСсылкаТипаЗначений, Свойства);
		ПоТипамЗначений.Вставить(Свойства.ТипЗначений, Свойства);
		ПоТипамГруппИЗначений.Вставить(Свойства.ТипЗначений, Свойства);
		Если Свойства.ТипГруппЗначений <> Тип("Неопределено") Тогда
			ПоТипамГруппИЗначений.Вставить(Свойства.ТипГруппЗначений, Свойства);
		КонецЕсли;
		
		Для Каждого Строка Из ВидДоступа.ДополнительныеТипы Цикл
			ПоТипамЗначений.Вставить(Строка.ТипЗначений, Свойства);
			ПоТипамГруппИЗначений.Вставить(Строка.ТипЗначений, Свойства);
			Если Строка.ТипГруппЗначений <> Тип("Неопределено") Тогда
				ПоТипамГруппИЗначений.Вставить(Строка.ТипГруппЗначений, Свойства);
			КонецЕсли;
		КонецЦикла;
		
	КонецЦикла;
	
	БезГруппДляЗначенияДоступа      = Новый Массив;
	СОднойГруппойДляЗначенияДоступа = Новый Массив;
	ТипыЗначенийДоступаСГруппами    = Новый Соответствие;
	
	ВидыДоступаСГруппами = Новый Соответствие;
	
	Для Каждого КлючИЗначение Из ЗначенияДоступаСГруппами.ПоТипамСсылок Цикл
		ВидДоступа = КлючИЗначение.Значение; // См. СвойстваВидаДоступа
		ИмяВидаДоступа = ВидДоступа.Имя;
		ВидыДоступаСГруппами.Вставить(ИмяВидаДоступа, Истина);
		
		ПустаяСсылка = ПустаяСсылкаОбъектаМетаданных(КлючИЗначение.Ключ);
		ТипыЗначенийДоступаСГруппами.Вставить(ТипЗнч(ПустаяСсылка), ПустаяСсылка);
		
		Если НЕ КлючИЗначение.Значение.НесколькоГруппЗначений
		   И СОднойГруппойДляЗначенияДоступа.Найти(ИмяВидаДоступа) = Неопределено Тогда
		   
			СОднойГруппойДляЗначенияДоступа.Добавить(ИмяВидаДоступа);
		КонецЕсли;
	КонецЦикла;
	
	ТипыЗначенийДоступаСГруппами.Вставить(Тип("СправочникСсылка.Пользователи"),
		Справочники.Пользователи.ПустаяСсылка());
	
	ТипыЗначенийДоступаСГруппами.Вставить(Тип("СправочникСсылка.ГруппыПользователей"),
		Справочники.ГруппыПользователей.ПустаяСсылка());
	
	ТипыЗначенийДоступаСГруппами.Вставить(Тип("СправочникСсылка.ВнешниеПользователи"),
		Справочники.ВнешниеПользователи.ПустаяСсылка());
	
	ТипыЗначенийДоступаСГруппами.Вставить(Тип("СправочникСсылка.ГруппыВнешнихПользователей"),
		Справочники.ГруппыВнешнихПользователей.ПустаяСсылка());
		
	ПоТипамЗначенийСИерархией = Новый Соответствие;
	Для Каждого КлючИЗначение Из ПоТипамЗначений Цикл
		ТипСсылка = КлючИЗначение.Ключ;
		Если ТипыЗначенийДоступаСГруппами[ТипСсылка] <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		
		ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипСсылка);
		Если ОбъектМетаданных <> Неопределено 
		   И (ОбщегоНазначения.ЭтоСправочник(ОбъектМетаданных)
		      Или ОбщегоНазначения.ЭтоПланВидовХарактеристик(ОбъектМетаданных))
		   И ОбъектМетаданных.Иерархический Тогда
			
			ТипОбъект = СтандартныеПодсистемыСервер.ТипОбъектаИлиНабораЗаписейОбъектаМетаданных(ОбъектМетаданных);
			ПоТипамЗначенийСИерархией.Вставить(ТипСсылка, КлючИЗначение.Значение);
			ПоТипамЗначенийСИерархией.Вставить(ТипОбъект, КлючИЗначение.Значение);
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого СвойстваВидаДоступа Из МассивСвойств Цикл
		Если ВидыДоступаСГруппами.Получить(СвойстваВидаДоступа.Имя) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Если СвойстваВидаДоступа.Имя = "Пользователи"
		 ИЛИ СвойстваВидаДоступа.Имя = "ВнешниеПользователи" Тогда
			Продолжить;
		КонецЕсли;
		БезГруппДляЗначенияДоступа.Добавить(СвойстваВидаДоступа.Имя);
	КонецЦикла;
	
	ПроверитьТипыПодпискиОбновитьГруппыЗначенийДоступа(ЗначенияДоступаСГруппами);
	
	// Окончательная фиксация данных.
	
	ЗначенияДоступаСГруппами.ИменаТаблицДляОбновления =
		Новый ФиксированныйМассив(ЗначенияДоступаСГруппами.ИменаТаблицДляОбновления);
		
	ЗначенияДоступаСГруппами.ТипыГруппЗначенийДляОбновления =
		Новый ФиксированноеСоответствие(ЗначенияДоступаСГруппами.ТипыГруппЗначенийДляОбновления);
	
	Для Каждого ОписаниеСвойства Из ЗначенияДоступаСГруппами Цикл
		ТекущееСоответствие = ОписаниеСвойства.Значение;
		Если ТипЗнч(ТекущееСоответствие) <> Тип("Соответствие") Тогда
			Продолжить;
		КонецЕсли;
		Для Каждого КлючИЗначение Из ТекущееСоответствие Цикл
			ТекущееСоответствие.Вставить(КлючИЗначение.Ключ,
				ВсеСвойства.Получить(КлючИЗначение.Значение));
		КонецЦикла;
		ЗначенияДоступаСГруппами[ОписаниеСвойства.Ключ] =
			Новый ФиксированноеСоответствие(ТекущееСоответствие);
	КонецЦикла;
	
	СвойстваВидовДоступа = Новый Структура;
	СвойстваВидовДоступа.Вставить("Массив",
		Новый ФиксированныйМассив(МассивСвойств));
	
	СвойстваВидовДоступа.Вставить("ПоИменам",
		Новый ФиксированноеСоответствие(ПоИменам));
	
	СвойстваВидовДоступа.Вставить("ПоСсылкам",
		Новый ФиксированноеСоответствие(ПоСсылкам));
	
	СвойстваВидовДоступа.Вставить("ПоТипамЗначений",
		Новый ФиксированноеСоответствие(ПоТипамЗначений));
	
	СвойстваВидовДоступа.Вставить("ПоТипамЗначенийСИерархией",
		Новый ФиксированноеСоответствие(ПоТипамЗначенийСИерархией));
	
	СвойстваВидовДоступа.Вставить("ПоТипамГруппИЗначений",
		Новый ФиксированноеСоответствие(ПоТипамГруппИЗначений));
	
	СвойстваВидовДоступа.Вставить("ЗначенияДоступаСГруппами",
		Новый ФиксированнаяСтруктура(ЗначенияДоступаСГруппами));
	
	СвойстваВидовДоступа.Вставить("БезГруппДляЗначенияДоступа",
		Новый ФиксированныйМассив(БезГруппДляЗначенияДоступа));
	
	СвойстваВидовДоступа.Вставить("СОднойГруппойДляЗначенияДоступа",
		Новый ФиксированныйМассив(СОднойГруппойДляЗначенияДоступа));
	
	СвойстваВидовДоступа.Вставить("ТипыЗначенийДоступаСГруппами",
		Новый ФиксированноеСоответствие(ТипыЗначенийДоступаСГруппами));
	
	Результат = Новый ФиксированнаяСтруктура(СвойстваВидовДоступа);
	
	ХешСуммы.ХешСуммаТиповГруппИЗначенийДоступа =
		ХешСуммаТиповГруппИЗначенийДоступа(СвойстваВидовДоступа);
	
	СтрокаВерсии = СтрСоединить(ОписаниеВерсии.СвойстваВерсии, Символы.ПС);
	ХешСуммы.ХешСумма = ХешСуммаДанных(СтрокаВерсии);
	
	Возврат Результат;
	
КонецФункции

// Для функции СвойстваВидовДоступа.
//
// Возвращаемое значение:
//   см. СвойстваВидаДоступа
// 
Функция НовыеСвойстваВидаДоступа(ВидДоступа, ПустаяСсылкаТипаЗначений)
	
	Свойства = Новый Структура;
	Свойства.Вставить("Имя",                      ВидДоступа.Имя);
	Свойства.Вставить("Ссылка",                   ПустаяСсылкаТипаЗначений);
	Свойства.Вставить("ТипЗначений",              ВидДоступа.ТипЗначений);
	Свойства.Вставить("ТипГруппЗначений",         ВидДоступа.ТипГруппЗначений);
	Свойства.Вставить("НесколькоГруппЗначений",   ВидДоступа.НесколькоГруппЗначений);
	Свойства.Вставить("ДополнительныеТипы");
	Свойства.Вставить("ТипыВыбираемыхЗначений");
	
	Возврат Свойства;
	
КонецФункции

// Для функции СвойстваВидовДоступа.
//
// Возвращаемое значение:
//   Структура:
//     * ПоТипам - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - см. СвойстваВидаДоступа
//     * ПоТипамСсылок - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - см. СвойстваВидаДоступа
//     * ИменаТаблицДляОбновления - ФиксированныйМассив из Строка
//     * ТипыГруппЗначенийДляОбновления - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - СправочникСсылка
//     * ПоТипамСсылокДляОбновления - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - см. СвойстваВидаДоступа
// 
Функция ЗначенияДоступаСГруппами()
	
	ЗначенияДоступаСГруппами = Новый Структура;
	ЗначенияДоступаСГруппами.Вставить("ПоТипам",                        Новый Соответствие);
	ЗначенияДоступаСГруппами.Вставить("ПоТипамСсылок",                  Новый Соответствие);
	ЗначенияДоступаСГруппами.Вставить("ИменаТаблицДляОбновления",       Новый Массив);
	ЗначенияДоступаСГруппами.Вставить("ТипыГруппЗначенийДляОбновления", Новый Соответствие);
	ЗначенияДоступаСГруппами.Вставить("ПоТипамДляОбновления",           Новый Соответствие);
	ЗначенияДоступаСГруппами.Вставить("ПоТипамСсылокДляОбновления",     Новый Соответствие);
	
	Возврат ЗначенияДоступаСГруппами;
	
КонецФункции

// Для функции СвойстваВидовДоступа.
//
// Возвращаемое значение:
//   ФиксированнаяСтруктура:
//     * ТипЗначений - Тип
//     * ТипГруппЗначений - Тип
//     * НесколькоГруппЗначений - Булево
//
Функция ДополнительныйТипВидаДоступа(Строка)
	
	Элемент = Новый Структура;
	Элемент.Вставить("ТипЗначений",            Строка.ТипЗначений);
	Элемент.Вставить("ТипГруппЗначений",       Строка.ТипГруппЗначений);
	Элемент.Вставить("НесколькоГруппЗначений", Строка.НесколькоГруппЗначений);
	
	Возврат Новый ФиксированнаяСтруктура(Элемент);
	
КонецФункции

// Для функции СвойстваВидовДоступа.
Процедура ЗаполнитьЗначенияДоступаСГруппами(Строка, ЗначенияДоступаСГруппами, Свойства, ТипыВыбираемыхЗначений)
	
	Если Свойства.Имя = "Пользователи" Тогда
		ДобавитьВМассив(ТипыВыбираемыхЗначений, Тип("СправочникСсылка.Пользователи"));
		ДобавитьВМассив(ТипыВыбираемыхЗначений, Тип("СправочникСсылка.ГруппыПользователей"));
		Возврат;
	КонецЕсли;
	
	Если Свойства.Имя = "ВнешниеПользователи" Тогда
		ДобавитьВМассив(ТипыВыбираемыхЗначений, Тип("СправочникСсылка.ВнешниеПользователи"));
		ДобавитьВМассив(ТипыВыбираемыхЗначений, Тип("СправочникСсылка.ГруппыВнешнихПользователей"));
		Возврат;
	КонецЕсли;
	
	ТипСсылки = Строка.ТипЗначений;
	
	МетаданныеТипаЗначений = Метаданные.НайтиПоТипу(Строка.ТипЗначений);
	Если ОбщегоНазначения.ЭтоПеречисление(МетаданныеТипаЗначений) Тогда
		ТипОбъекта = ТипСсылки;
	Иначе
		ТипОбъекта = СтандартныеПодсистемыСервер.ТипОбъектаИлиНабораЗаписейОбъектаМетаданных(
			МетаданныеТипаЗначений);
	КонецЕсли;
	
	Если Строка.ТипГруппЗначений = Тип("Неопределено") Тогда
		ДобавитьТипыПодпискиОбновитьГруппыЗначенийДоступа(ТипСсылки,
			ТипОбъекта, МетаданныеТипаЗначений, ЗначенияДоступаСГруппами, Свойства, Неопределено);
		ДобавитьВМассив(ТипыВыбираемыхЗначений, Строка.ТипЗначений);
		Возврат;
	КонецЕсли;
	
	Если Строка.ТипГруппЗначений <> Тип("Неопределено") Тогда
		ДобавитьВМассив(ТипыВыбираемыхЗначений, Строка.ТипГруппЗначений);
	КонецЕсли;
	
	ЗначенияДоступаСГруппами.ПоТипам.Вставить(ТипСсылки,  Свойства);
	ЗначенияДоступаСГруппами.ПоТипам.Вставить(ТипОбъекта, Свойства);
	ЗначенияДоступаСГруппами.ПоТипамСсылок.Вставить(ТипСсылки, Свойства);
	
	МетаданныеТипаГруппЗначений = Метаданные.НайтиПоТипу(Строка.ТипГруппЗначений);
	
	ДобавитьТипыПодпискиОбновитьГруппыЗначенийДоступа(ТипСсылки,
		ТипОбъекта, МетаданныеТипаЗначений, ЗначенияДоступаСГруппами, Свойства, МетаданныеТипаГруппЗначений);
	
КонецПроцедуры

// Для функции СвойстваВидовДоступа и процедуры ЗаполнитьЗначенияДоступаСГруппами.
Процедура ДобавитьТипыПодпискиОбновитьГруппыЗначенийДоступа(ТипСсылки, ТипОбъекта,
			МетаданныеТипаЗначений, ЗначенияДоступаСГруппами, Свойства, МетаданныеТипаГруппЗначений)
	
	ПустаяСсылкаТипаЗначений = ПредопределенноеЗначение(МетаданныеТипаЗначений.ПолноеИмя() + ".ПустаяСсылка");
	
	Если МетаданныеТипаГруппЗначений = Неопределено Тогда
		ПустаяСсылкаТипаГруппЗначений = ПустаяСсылкаТипаЗначений;
	Иначе
		ПустаяСсылкаТипаГруппЗначений = ПустаяСсылкаОбъектаМетаданных(МетаданныеТипаГруппЗначений);
	КонецЕсли;
	
	ЗначенияДоступаСГруппами.ИменаТаблицДляОбновления.Добавить(МетаданныеТипаЗначений.ПолноеИмя());
	
	ЗначенияДоступаСГруппами.ТипыГруппЗначенийДляОбновления.Вставить(ТипСсылки,
		ПустаяСсылкаТипаГруппЗначений);
	
	ЗначенияДоступаСГруппами.ТипыГруппЗначенийДляОбновления.Вставить(ПустаяСсылкаТипаЗначений,
		ПустаяСсылкаТипаГруппЗначений);
	
	ЗначенияДоступаСГруппами.ПоТипамСсылокДляОбновления.Вставить(ТипСсылки, Свойства);
	ЗначенияДоступаСГруппами.ПоТипамДляОбновления.Вставить(ТипСсылки, Свойства);
	ЗначенияДоступаСГруппами.ПоТипамДляОбновления.Вставить(ТипОбъекта, Свойства);
	
КонецПроцедуры

// Для функции СвойстваВидовДоступа.
Процедура ПроверитьТипыПодпискиОбновитьГруппыЗначенийДоступа(ЗначенияДоступаСГруппами)
	
	ТекущиеТипыПодписки = УправлениеДоступомСлужебныйПовтИсп.ТипыПоляТаблицы(
		"ОпределяемыйТип.ЗначениеДоступаОбъект");
	
	ТребуемыеТипыПодписки = Новый Соответствие;
	Для Каждого КлючИЗначение Из ЗначенияДоступаСГруппами.ПоТипамДляОбновления Цикл
		Если ЗначенияДоступаСГруппами.ПоТипамСсылокДляОбновления.Получить(КлючИЗначение.Ключ) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ТребуемыеТипыПодписки.Вставить(КлючИЗначение.Ключ, Истина);
	КонецЦикла;
	
	НедостающиеТипы = Новый Массив;
	
	Для Каждого КлючИЗначение Из ТребуемыеТипыПодписки Цикл
		Если ТекущиеТипыПодписки.Получить(КлючИЗначение.Ключ) = Неопределено Тогда
			НедостающиеТипы.Добавить(КлючИЗначение.Ключ);
		КонецЕсли;
	КонецЦикла;
	
	Если НедостающиеТипы.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'По данным, полученным из процедуры %1
		           |общего модуля %2,
		           |в определяемом типе %3 не указаны требуемые типы:
		           |- %4'"),
		"ПриЗаполненииВидовДоступа",
		"УправлениеДоступомПереопределяемый",
		"ЗначениеДоступаОбъект",
		СтрСоединить(НедостающиеТипы, "," + Символы.ПС + "- "));
	
	ВызватьИсключение ТекстОшибки;
	
КонецПроцедуры

// Для функции СвойстваВидовДоступа.
Процедура ПроверитьТип(ВидДоступа, Тип, ВсеТипы, Параметры, ПроверкаТиповГрупп = Ложь, ПроверкаПересечения = Ложь)
	
	Если Тип = Тип("Неопределено") Тогда
		Если ПроверкаТиповГрупп Тогда
			Возврат;
		КонецЕсли;
		ТекстОшибки = Параметры.ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для вида доступа ""%1"" не указан тип значений доступа.'"),
			ВидДоступа.Имя);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	// Проверка, что указан тип ссылки.
	Если НЕ ОбщегоНазначения.ЭтоСсылка(Тип) Тогда
		Если ПроверкаТиповГрупп Тогда
			ОписаниеОшибки =
				НСтр("ru = 'Тип ""%1"" указан, как тип групп значений, для вида доступа ""%2"".
				           |Однако это не тип ссылки.'");
		Иначе
			ОписаниеОшибки =
				НСтр("ru = 'Тип ""%1"" указан, как тип значений, для вида доступа ""%2"".
				           |Однако это не тип ссылки.'");
		КонецЕсли;
		ТекстОшибки = Параметры.ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ОписаниеОшибки, Тип, ВидДоступа.Имя);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	// Проверка повторения и пересечения типов значений и групп значений.
	ДляТогоЖеВидаДоступаОшибкиНет = Ложь;
	
	Если ПроверкаТиповГрупп Тогда
		Если ПроверкаПересечения Тогда
			ОписаниеОшибки =
				НСтр("ru = 'Тип ""%1"" указан, как тип значений, для вида доступа ""%2"".
				           |Для вида доступа ""%3"" его нельзя указать, как тип групп значений.'");
		Иначе
			ДляТогоЖеВидаДоступаОшибкиНет = Истина;
			ОписаниеОшибки =
				НСтр("ru = 'Тип групп значений ""%1"" уже указан для вида доступа ""%2"".
				           |Для вида доступа ""%3"" его нельзя указать.'");
		КонецЕсли;
	Иначе
		Если ПроверкаПересечения Тогда
			ОписаниеОшибки =
				НСтр("ru = 'Тип ""%1"" указан, как тип групп значений, для вида доступа ""%2"".
				           |Для вида доступа ""%3"" его нельзя указать, как тип значений.'");
		Иначе
			ОписаниеОшибки =
				НСтр("ru = 'Тип значений ""%1"" уже указан для вида доступа ""%2"".
				           |Для вида доступа ""%3"" его нельзя указать.'");
		КонецЕсли;
	КонецЕсли;
	
	Если ВсеТипы.Получить(Тип) <> Неопределено Тогда
		Если НЕ (ДляТогоЖеВидаДоступаОшибкиНет И ВидДоступа.Имя = ВсеТипы.Получить(Тип)) Тогда
			ТекстОшибки = Параметры.ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				ОписаниеОшибки, Тип, ВсеТипы.Получить(Тип), ВидДоступа.Имя);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
	ИначеЕсли НЕ ПроверкаПересечения Тогда
		ВсеТипы.Вставить(Тип, ВидДоступа.Имя);
	КонецЕсли;
	
	// Проверка состава определяемых типов.
	ОписаниеОшибки = "";
	Если Параметры.ОпределяемыеТипыЗначенийДоступа.Получить(Тип) = Неопределено Тогда
		Если ПроверкаТиповГрупп Тогда
			ОписаниеОшибки =
				НСтр("ru = 'Тип групп значений доступа ""%1"" вида доступа ""%2""
				           |не указан в определяемом типе %3.'");
		Иначе
			ОписаниеОшибки =
				НСтр("ru = 'Тип значений доступа ""%1"" вида доступа ""%2""
				           |не указан в определяемом типе %3.'");
		КонецЕсли;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ОписаниеОшибки) Тогда
		ТекстОшибки = Параметры.ЗаголовокОшибки + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			ОписаниеОшибки, Тип, ВидДоступа.Имя, "ЗначениеДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//    * Ключ - Тип
//    * Значение - Строка
//
Функция ПредставлениеВидовДоступа() Экспорт
	
	ВидыДоступа = ЗаполненныеВидыДоступаСеанса();
	
	ПредставлениеВидовДоступа = Новый Соответствие;
	
	Для Каждого ВидДоступа Из ВидыДоступа Цикл
		ПредставлениеВидовДоступа.Вставить(ВидДоступа.ТипЗначений, ВидДоступа.Представление);
	КонецЦикла;
	
	Возврат Новый ФиксированноеСоответствие(ПредставлениеВидовДоступа);
	
КонецФункции

Функция ПредставлениеВидаДоступа(СвойстваВидаДоступа) Экспорт
	
	ПредставлениеВидовДоступа = УправлениеДоступомСлужебныйПовтИсп.ПредставлениеВидовДоступа();
	
	Представление = ПредставлениеВидовДоступа.Получить(СвойстваВидаДоступа.ТипЗначений);
	
	Если Не ЗначениеЗаполнено(Представление) Тогда
		Представление = СвойстваВидаДоступа.Имя;
	КонецЕсли;
	
	Возврат Представление;
	
КонецФункции

// Для процедуры ЗаполнитьЗначенияДоступаСГруппами.
Процедура ДобавитьВМассив(Массив, Значение)
	
	Если Массив.Найти(Значение) = Неопределено Тогда
		Массив.Добавить(Значение);
	КонецЕсли;
	
КонецПроцедуры

// Для функций УправлениеДоступомСлужебныйПовтИсп.ОписаниеСвойствВидовДоступаСеанса,
// СвойстваВидовДоступа и процедур ОбновитьОписаниеСвойствВидовДоступа,
// ОбновитьГруппыИНаборыЗначенийДоступаПриИзмененииТиповГруппИЗначений.
//
// Параметры:
//  Значение - Неопределено
//           - ФиксированнаяСтруктура
//
// Возвращаемое значение:
//  Структура:
//   * ХешСумма - Строка
//   * ХешСуммаТиповГруппИЗначенийДоступа - Строка
//
Функция НовыеХешСуммыСвойствВидовДоступа(Значение = Неопределено) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ХешСумма", "");
	Результат.Вставить("ХешСуммаТиповГруппИЗначенийДоступа", "");
	
	Если ТипЗнч(Значение) = Тип("ФиксированнаяСтруктура") Тогда
		ЗаполнитьЗначенияСвойств(Результат, Значение);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Для процедуры ПроверенныеСвойстваВидовДоступаСеанса.
Функция ХешСуммаТиповГруппИЗначенийДоступа(СвойстваВидовДоступа)
	
	Данные = Новый Массив;
	Данные.Добавить(ОписаниеТиповИзКлючейСоответствия(СвойстваВидовДоступа.ПоТипамЗначений));
	Данные.Добавить(ОписаниеТиповИзКлючейСоответствия(СвойстваВидовДоступа.ТипыЗначенийДоступаСГруппами));
	
	ИменаТаблицДляОбновления = Новый СписокЗначений;
	ИменаТаблицДляОбновления.ЗагрузитьЗначения(Новый Массив(
		СвойстваВидовДоступа.ЗначенияДоступаСГруппами.ИменаТаблицДляОбновления));
	ИменаТаблицДляОбновления.СортироватьПоЗначению();
	
	Данные.Добавить(ИменаТаблицДляОбновления.ВыгрузитьЗначения());
	
	Данные.Добавить(Метаданные.ОпределяемыеТипы.ВладелецНаборовЗначенийДоступаОбъект.Тип);
	
	Возврат ХешСуммаДанных(Данные);
	
КонецФункции

// Для функции ХешСуммаТиповГруппИЗначенийДоступа.
Функция ОписаниеТиповИзКлючейСоответствия(Данные)
	
	Типы = Новый Массив;
	Для Каждого КлючИЗначение Из Данные Цикл
		Типы.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	
	Возврат Новый ОписаниеТипов(Типы);
	
КонецФункции

Функция ХешСуммаДанных(Данные) Экспорт
	
	СтрокаДляХеширования = СтрокаДанныхДляХеширования(Данные);
	 
	Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256);
	Хеширование.Добавить(СтрокаДляХеширования);
	ХешСумма = Хеширование.ХешСумма;
	СтрокаХешСуммы = Base64Строка(ХешСумма);
	
	Возврат СтрокаХешСуммы;
	
КонецФункции

Функция ГруппыДоступаИспользующиеИерархиюЗначенийДоступа(ТипЗначенияВидаДоступа)
	
	УстановитьПривилегированныйРежим(Истина);
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ГруппыДоступа.Ссылка КАК Ссылка,
	|	ГруппыДоступа.Профиль КАК Профиль
	|ПОМЕСТИТЬ ГруппыДоступа
	|ИЗ
	|	Справочник.ГруппыДоступа КАК ГруппыДоступа
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|		ПО ГруппыДоступа.Профиль = ПрофилиГруппДоступа.Ссылка
	|			И (ГруппыДоступа.Профиль <> &ПрофильАдминистратор)
	|			И (НЕ ГруппыДоступа.ПометкаУдаления)
	|			И (НЕ ПрофилиГруппДоступа.ПометкаУдаления)
	|			И (ИСТИНА В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ИСТИНА КАК ЗначениеИстина
	|				ИЗ
	|					Справочник.ГруппыДоступа.Пользователи КАК УчастникиГруппДоступа
	|				ГДЕ
	|					УчастникиГруппДоступа.Ссылка = ГруппыДоступа.Ссылка))
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Ссылка,
	|	ГруппыДоступа.Профиль
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ГруппыДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ГруппыДоступа.ЗначенияДоступа КАК ГруппыДоступаЗначенияДоступа
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ГруппыДоступа КАК ГруппыДоступа
	|		ПО ГруппыДоступаЗначенияДоступа.Ссылка = ГруппыДоступа.Ссылка
	|ГДЕ
	|	ГруппыДоступаЗначенияДоступа.ВключаяНижестоящие
	|	И ТИПЗНАЧЕНИЯ(ГруппыДоступаЗначенияДоступа.ЗначениеДоступа) = &ТипЗначенияВидаДоступа
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ГруппыДоступа.Ссылка
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа.ЗначенияДоступа КАК ПрофилиГруппДоступаЗначенияДоступа
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ГруппыДоступа КАК ГруппыДоступа
	|		ПО ПрофилиГруппДоступаЗначенияДоступа.Ссылка = ГруппыДоступа.Профиль
	|ГДЕ
	|	ПрофилиГруппДоступаЗначенияДоступа.ВключаяНижестоящие
	|	И ТИПЗНАЧЕНИЯ(ПрофилиГруппДоступаЗначенияДоступа.ЗначениеДоступа) = &ТипЗначенияВидаДоступа";
	
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("ТипЗначенияВидаДоступа", ТипЗначенияВидаДоступа);
	Запрос.УстановитьПараметр("ПрофильАдминистратор", УправлениеДоступом.ПрофильАдминистратор());
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Возвращает таблицу значений, содержащую вид ограничений доступа по каждому праву
// объектов метаданных.
//  Если записи по праву нет, значит ограничений по праву нет.
//  Таблица содержит только виды доступа, заданные разработчиком,
// исходя из их применения в текстах ограничений.
//  Для получения всех видов доступа, включая используемые в наборах
// значений доступа может быть использовано
// текущее состояние регистра сведений НаборыЗначенийДоступа.
//
// Параметры:
//  ДляПроверки - Булево - вернуть текстовое описание ограничений прав, заполненное
//                         в переопределяемых модулях без проверки.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * ДляВнешнихПользователей - Булево - если Ложь, тогда ограничение для пользователей,
//                                 если Истина, тогда для внешних пользователей.
//                                 Колонка присутствует только для универсального ограничения.
//   * ПолноеИмя      - Строка - полное имя таблицы.
//   * Таблица        - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                    - СправочникСсылка.ИдентификаторыОбъектовРасширений - идентификатор таблицы.
//   * ВидДоступа     - ЛюбаяСсылка - пустая ссылка основного типа значений вида доступа,
//                              пустая ссылка владельца настроек прав.
//   * Право          - Строка - "Чтение", "Изменение".
//                    - Неопределено - для вида доступа Объект.
//   * ТаблицаОбъекта - ЛюбаяСсылка - пустая ссылка объекта метаданных, через который ограничивается доступ,
//                      используя наборы значений доступа, например, Справочник.ПапкиФайлов.
//                    - Неопределено - если ВидДоступа <> Неопределено.
//                      Колонка присутствует только для стандартного ограничения.
//
//  Строка - когда ДляПроверки равно Истина, тогда ограничения прав, как они добавлены в переопределяемом модуле.
//
Функция ПостоянныеВидыОграниченийПравОбъектовМетаданных(ДляПроверки = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	
	УниверсальноеОграничение = ОграничиватьДоступНаУровнеЗаписейУниверсально(Истина, Истина);
	
	Если ДляПроверки Или Не УниверсальноеОграничение Тогда
		ОграниченияПрав = "";
		ИнтеграцияПодсистемБСП.ПриЗаполненииВидовОграниченийПравОбъектовМетаданных(ОграниченияПрав);
		УправлениеДоступомПереопределяемый.ПриЗаполненииВидовОграниченийПравОбъектовМетаданных(ОграниченияПрав);
		Если ДляПроверки Тогда
			Возврат ОграниченияПрав;
		КонецЕсли;
	КонецЕсли;
	
	ТипыИдентификаторов = Новый Массив;
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных"));
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений"));
	
	ВидыОграниченийПрав = Новый ТаблицаЗначений;
	ВидыОграниченийПрав.Колонки.Добавить("ДляВнешнихПользователей", Новый ОписаниеТипов("Булево"));
	ВидыОграниченийПрав.Колонки.Добавить("ПолноеИмя",  Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(430)));
	ВидыОграниченийПрав.Колонки.Добавить("Таблица",    Новый ОписаниеТипов(ТипыИдентификаторов));
	ВидыОграниченийПрав.Колонки.Добавить("Право",      Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));
	ВидыОграниченийПрав.Колонки.Добавить("ВидДоступа", УправлениеДоступомСлужебныйПовтИсп.ОписаниеТиповЗначенийДоступаИВладельцевНастроекПрав());
	ВидыОграниченийПрав.Колонки.Добавить("ТаблицаОбъекта",
		Метаданные.РегистрыСведений.НаборыЗначенийДоступа.Измерения.Объект.Тип);
	ВидыОграниченийПрав.Колонки.Добавить("ВедущееПраво", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));
	
	ВидыДоступаПоИменам = СвойстваВидовДоступа().ПоИменам;
	
	Если УниверсальноеОграничение Тогда
		ВсеВидыОграничений = ВсеВидыОграниченийПравДляОтчетаПраваДоступа();
		ДобавитьВидыОграниченияПрав(ВидыОграниченийПрав,
			ВсеВидыОграничений.ДляПользователей, ВидыДоступаПоИменам, Истина, Ложь);
		ДобавитьВидыОграниченияПрав(ВидыОграниченийПрав,
			ВсеВидыОграничений.ДляВнешнихПользователей, ВидыДоступаПоИменам, Истина, Истина);
	Иначе
		ДобавитьВидыОграниченияПрав(ВидыОграниченийПрав,
			ОграниченияПрав, ВидыДоступаПоИменам, Ложь);
	КонецЕсли;
	
	ПолныеИмена = ВидыОграниченийПрав.ВыгрузитьКолонку("ПолноеИмя");
	ИдентификаторыИмен = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(ПолныеИмена);
	Для Каждого Строка Из ВидыОграниченийПрав Цикл
		Строка.Таблица = ИдентификаторыИмен.Получить(Строка.ПолноеИмя);
	КонецЦикла;
	
	// Добавление видов доступа объектов, которые определяются не только через наборы значений доступа.
	Если Не УниверсальноеОграничение Тогда
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ЗависимостиПравДоступа.ПодчиненнаяТаблица,
		|	ЗависимостиПравДоступа.ТипВедущейТаблицы
		|ИЗ
		|	РегистрСведений.ЗависимостиПравДоступа КАК ЗависимостиПравДоступа";
		ЗависимостиПрав = Запрос.Выполнить().Выгрузить();
	КонецЕсли;
	
	Контекст = Новый Структура;
	Контекст.Вставить("УниверсальноеОграничение", УниверсальноеОграничение);
	Контекст.Вставить("ВидыОграниченийПрав",      ВидыОграниченийПрав);
	Контекст.Вставить("ЗависимостиПрав",          ЗависимостиПрав);
	Контекст.Вставить("ТекущиеЗависимыеТаблицы",  Новый Соответствие);
	Контекст.Вставить("ВидыДоступаВедущихТаблиц", Новый Соответствие);
	Отбор = Новый Структура("ВидДоступа", Неопределено);
	ВидыДоступаОбъект = ВидыОграниченийПрав.НайтиСтроки(Отбор);
	Отбор = Новый Структура("ДляВнешнихПользователей, ПолноеИмя, Таблица, Право, ВидДоступа");
	Для Каждого СтрокаВидДоступаОбъект Из ВидыДоступаОбъект Цикл
		ВидыДоступа = ВидыДоступаВедущейТаблицы(Контекст, СтрокаВидДоступаОбъект);
		Для Каждого ВидДоступа Из ВидыДоступа Цикл
			ЗаполнитьЗначенияСвойств(Отбор, СтрокаВидДоступаОбъект);
			Отбор.ВидДоступа = ВидДоступа;
			Если ВидыОграниченийПрав.НайтиСтроки(Отбор).Количество() = 0 Тогда
				ЗаполнитьЗначенияСвойств(ВидыОграниченийПрав.Добавить(), Отбор);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	ВидыОграниченийПрав.Колонки.Удалить("ВедущееПраво");
	Если УниверсальноеОграничение Тогда
		ВидыОграниченийПрав.Колонки.Удалить("ТаблицаОбъекта");
	Иначе
		ВидыОграниченийПрав.Колонки.Удалить("ДляВнешнихПользователей");
	КонецЕсли;
	
	Возврат ВидыОграниченийПрав;
	
КонецФункции

// Для функции ПостоянныеВидыОграниченийПравОбъектовМетаданных.
Функция ВидыДоступаВедущейТаблицы(Контекст, СтрокаВидДоступаОбъект)
	
	ИдентификаторТаблицы = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		ТипЗнч(СтрокаВидДоступаОбъект.ТаблицаОбъекта));
	
	Если Контекст.УниверсальноеОграничение Тогда
		ВедущееПраво = СтрокаВидДоступаОбъект.ВедущееПраво;
	Иначе
		Отбор = Новый Структура;
		Отбор.Вставить("ПодчиненнаяТаблица", СтрокаВидДоступаОбъект.Таблица);
		Отбор.Вставить("ТипВедущейТаблицы", СтрокаВидДоступаОбъект.ТаблицаОбъекта);
		Если Контекст.ЗависимостиПрав.НайтиСтроки(Отбор).Количество() = 0 Тогда
			ВедущееПраво = СтрокаВидДоступаОбъект.Право;
		Иначе
			ВедущееПраво = "Чтение";
		КонецЕсли;
	КонецЕсли;
	
	Ключ = ?(СтрокаВидДоступаОбъект.ДляВнешнихПользователей, "1:", "0:")
		+ Строка(ИдентификаторТаблицы.УникальныйИдентификатор()) + ":" + ВедущееПраво;
	
	Если Контекст.ТекущиеЗависимыеТаблицы.Получить(Ключ) <> Неопределено Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ВидыДоступа = Контекст.ВидыДоступаВедущихТаблиц.Получить(Ключ);
	Если ВидыДоступа <> Неопределено Тогда
		Возврат ВидыДоступа;
	КонецЕсли;
	
	Контекст.ТекущиеЗависимыеТаблицы.Вставить(Ключ, Истина);
	ВидыДоступа = Новый Массив;
	
	Отбор = Новый Структура("ДляВнешнихПользователей, Таблица, Право",
		СтрокаВидДоступаОбъект.ДляВнешнихПользователей, ИдентификаторТаблицы, ВедущееПраво);
	ВидыДоступаВедущейТаблицы = Контекст.ВидыОграниченийПрав.НайтиСтроки(Отбор);
	
	Для Каждого ОписаниеВидаДоступа Из ВидыДоступаВедущейТаблицы Цикл
		Если ОписаниеВидаДоступа.ВидДоступа = Неопределено Тогда
			ВидыДоступаВложеннойВедущейТаблицы = ВидыДоступаВедущейТаблицы(Контекст,
				ОписаниеВидаДоступа);
			Для Каждого ВидДоступа Из ВидыДоступаВложеннойВедущейТаблицы Цикл
				ДобавитьВидДоступаВедущейТаблицы(ВидыДоступа, ВидДоступа);
			КонецЦикла;
		Иначе
			ДобавитьВидДоступаВедущейТаблицы(ВидыДоступа, ОписаниеВидаДоступа.ВидДоступа);
		КонецЕсли;
	КонецЦикла;
	
	Контекст.ТекущиеЗависимыеТаблицы.Удалить(Ключ);
	Контекст.ВидыДоступаВедущихТаблиц.Вставить(Ключ, ВидыДоступа);
	
	Возврат ВидыДоступа;
	
КонецФункции

// Для функции ВидыДоступаВедущейТаблицы.
Процедура ДобавитьВидДоступаВедущейТаблицы(ВидыДоступа, ВидДоступа)
	
	Если ВидыДоступа.Найти(ВидДоступа) = Неопределено Тогда
		ВидыДоступа.Добавить(ВидДоступа);
	КонецЕсли;
	
КонецПроцедуры

// Для функции ПостоянныеВидыОграниченийПравОбъектовМетаданных.
Процедура ДобавитьВидыОграниченияПрав(ВидыОграниченийПрав, ОграниченияПрав, ВидыДоступаПоИменам,
			УниверсальноеОграничение, ДляВнешнихПользователей = Неопределено)
	
	Для НомерСтроки = 1 По СтрЧислоСтрок(ОграниченияПрав) Цикл
		ТекущаяСтрока = СокрЛП(СтрПолучитьСтроку(ОграниченияПрав, НомерСтроки));
		Если ЗначениеЗаполнено(ТекущаяСтрока) Тогда
			ПояснениеОшибки = "";
			ЧастиСтроки = СтрРазделить(ТекущаяСтрока, ".");
			Если Не УниверсальноеОграничение
			   И ЧастиСтроки.Количество() <> 4
			   И ЧастиСтроки.Количество() <> 6 Тогда
				ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Строка должна быть в формате %1.'"),
					"<ПолноеИмяТаблицы>.<ИмяПрава>.<ИмяВидаДоступа>[.<ПолноеИмяТаблицыОбъекта>]");
			ИначеЕсли УниверсальноеОграничение
			   И ЧастиСтроки.Количество() <> 4
			   И ЧастиСтроки.Количество() <> 7 Тогда
				ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Строка должна быть в формате %1.'"),
					"<ПолноеИмяТаблицы>.<ИмяПрава>.<ИмяВидаДоступа>[.<ПолноеИмяТаблицыОбъекта>.<ИмяВедущегоПрава>]");
			Иначе
				Таблица    = ЧастиСтроки[0] + "." + ЧастиСтроки[1];
				Право      = ЧастиСтроки[2];
				ВидДоступа = ЧастиСтроки[3];
				Если ЧастиСтроки.Количество() = 4 Тогда
					ТаблицаОбъекта = "";
					ВедущееПраво   = "";
				Иначе
					ТаблицаОбъекта = ЧастиСтроки[4] + "." + ЧастиСтроки[5];
					ВедущееПраво   = ?(УниверсальноеОграничение, ЧастиСтроки[6], "");
				КонецЕсли;
				
				ОбъектМетаданныхТаблицы = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Таблица);
				Если ОбъектМетаданныхТаблицы = Неопределено Тогда
					Если УниверсальноеОграничение Тогда
						Продолжить;
					КонецЕсли;
					ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не существует таблица ""%1"".'"), Таблица);
				
				ИначеЕсли Право <> "Чтение" И Право <> "Изменение" Тогда
					ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не существует право ""%1"".'"), Право);
				
				ИначеЕсли ВРег(ВидДоступа) = ВРег("Объект") Тогда
					Если ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ТаблицаОбъекта) = Неопределено Тогда
						Если УниверсальноеОграничение Тогда
							Продолжить;
						КонецЕсли;
						ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Не существует таблица объекта ""%1"".'"),
							ТаблицаОбъекта);
					Иначе
						ВидДоступаСсылка = Неопределено;
						ТаблицаОбъектаСсылка = ПустаяСсылкаОбъектаМетаданных(ТаблицаОбъекта);
						Если УниверсальноеОграничение
						   И ВедущееПраво <> "Чтение"
						   И ВедущееПраво <> "Изменение" Тогда
							ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
								НСтр("ru = 'Не существует ведущее право ""%1"".'"), ВедущееПраво);
						КонецЕсли;
					КонецЕсли;
					
				ИначеЕсли ВРег(ВидДоступа) = ВРег("НастройкиПрав") Тогда
					Если ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ТаблицаОбъекта) = Неопределено Тогда
						Если УниверсальноеОграничение Тогда
							Продолжить;
						КонецЕсли;
						ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Не существует таблица владельца настроек прав ""%1"".'"),
							ТаблицаОбъекта);
					Иначе
						ВидДоступаСсылка = ПустаяСсылкаОбъектаМетаданных(ТаблицаОбъекта);
						ТаблицаОбъектаСсылка = Неопределено;
					КонецЕсли;
				
				ИначеЕсли ВидыДоступаПоИменам.Получить(ВидДоступа) = Неопределено Тогда
					Если УниверсальноеОграничение Тогда
						Продолжить;
					КонецЕсли;
					ПояснениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не существует вид доступа ""%1"".'"), ВидДоступа);
				Иначе
					СвойстваВидаДоступа = ВидыДоступаПоИменам.Получить(ВидДоступа); // См. СвойстваВидаДоступа
					ВидДоступаСсылка = СвойстваВидаДоступа.Ссылка;
					ТаблицаОбъектаСсылка = Неопределено;
				КонецЕсли;
			КонецЕсли;
			
			Если ЗначениеЗаполнено(ПояснениеОшибки) Тогда
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Ошибка в строке описания вида ограничений права объекта метаданных:
						           |""%1"".'")
						+ Символы.ПС
						+ Символы.ПС,
						ТекущаяСтрока)
					+ ПояснениеОшибки;
				
				Если Не УниверсальноеОграничение Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Некорректное внедрение подсистемы %1
							           |в процедуре %2
							           |общего модуля %3.
							           |
							           |%4'"),
							"УправлениеДоступом",
							"ПриЗаполненииВидовОграниченийПравОбъектовМетаданных",
							"УправлениеДоступомПереопределяемый",
							ТекстОшибки);
				КонецЕсли;
				ВызватьИсключение ТекстОшибки;
			Иначе
				НовоеОписание = ВидыОграниченийПрав.Добавить();
				НовоеОписание.ДляВнешнихПользователей = ДляВнешнихПользователей;
				НовоеОписание.ПолноеИмя      = ОбъектМетаданныхТаблицы.ПолноеИмя();
				НовоеОписание.Право          = Право;
				НовоеОписание.ВидДоступа     = ВидДоступаСсылка;
				НовоеОписание.ТаблицаОбъекта = ТаблицаОбъектаСсылка;
				НовоеОписание.ВедущееПраво   = ВедущееПраво;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Только для внутреннего использования.
//
// Возвращаемое значение:
//   ФиксированноеСоответствие из КлючИЗначение:
//     * Ключ - Тип
//     * Значение - Строка
//
Функция ТипыГруппИЗначенийВидовДоступа() Экспорт
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	
	ТипыГруппИЗначенийВидовДоступа = Новый ТаблицаЗначений;
	ТипыГруппИЗначенийВидовДоступа.Колонки.Добавить("ВидДоступа",        Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	ТипыГруппИЗначенийВидовДоступа.Колонки.Добавить("ТипГруппИЗначений", Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	
	Для Каждого КлючИЗначение Из СвойстваВидовДоступа.ПоТипамГруппИЗначений Цикл
		Строка = ТипыГруппИЗначенийВидовДоступа.Добавить();
		СвойстваВидаДоступа = КлючИЗначение.Значение; // См. СвойстваВидаДоступа
		Строка.ВидДоступа = СвойстваВидаДоступа.Ссылка;
		
		Типы = Новый Массив;
		Типы.Добавить(КлючИЗначение.Ключ);
		ОписаниеТипа = Новый ОписаниеТипов(Типы);
		
		Строка.ТипГруппИЗначений = ОписаниеТипа.ПривестиЗначение(Неопределено);
	КонецЦикла;
	
	Возврат ТипыГруппИЗначенийВидовДоступа;
	
КонецФункции

// Только для внутреннего использования.
//
// Возвращаемое значение:
//   ТаблицаЗначений:
//     * ВидДоступа - ЛюбаяСсылка
//     * ТипЗначений - ЛюбаяСсылка
//
Функция ТипыЗначенийВидовДоступаИВладельцевНастроекПрав() Экспорт
	
	ТипыЗначенийВидовДоступаИВладельцевНастроекПрав = Новый ТаблицаЗначений;
	
	ТипыЗначенийВидовДоступаИВладельцевНастроекПрав.Колонки.Добавить("ВидДоступа",
		УправлениеДоступомСлужебныйПовтИсп.ОписаниеТиповЗначенийДоступаИВладельцевНастроекПрав());
	
	ТипыЗначенийВидовДоступаИВладельцевНастроекПрав.Колонки.Добавить("ТипЗначений",
		УправлениеДоступомСлужебныйПовтИсп.ОписаниеТиповЗначенийДоступаИВладельцевНастроекПрав());
	
	ТипыЗначенийВидовДоступа = ТипыЗначенийВидовДоступа();
	
	Для Каждого Строка Из ТипыЗначенийВидовДоступа Цикл
		ЗаполнитьЗначенияСвойств(ТипыЗначенийВидовДоступаИВладельцевНастроекПрав.Добавить(), Строка);
	КонецЦикла;
	
	ВозможныеПрава = ВозможныеПраваДляНастройкиПравОбъектов();
	ВладельцыПрав = ВозможныеПрава.ПоТипамСсылок;
	
	Для Каждого КлючИЗначение Из ВладельцыПрав Цикл
		
		Типы = Новый Массив;
		Типы.Добавить(КлючИЗначение.Ключ);
		ОписаниеТипа = Новый ОписаниеТипов(Типы);
		
		Строка = ТипыЗначенийВидовДоступаИВладельцевНастроекПрав.Добавить();
		Строка.ВидДоступа  = ОписаниеТипа.ПривестиЗначение(Неопределено);
		Строка.ТипЗначений = ОписаниеТипа.ПривестиЗначение(Неопределено);
	КонецЦикла;
	
	Возврат ТипыЗначенийВидовДоступаИВладельцевНастроекПрав;
	
КонецФункции

// Для функции ТипыЗначенийВидовДоступаИВладельцевНастроекПрав.
Функция ТипыЗначенийВидовДоступа()
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	
	ТипыЗначенийВидовДоступа = Новый ТаблицаЗначений;
	ТипыЗначенийВидовДоступа.Колонки.Добавить("ВидДоступа",  Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	ТипыЗначенийВидовДоступа.Колонки.Добавить("ТипЗначений", Метаданные.ОпределяемыеТипы.ЗначениеДоступа.Тип);
	
	Для каждого КлючИЗначение Из СвойстваВидовДоступа.ПоТипамЗначений Цикл
		Строка = ТипыЗначенийВидовДоступа.Добавить();
		СвойстваВидаДоступа = КлючИЗначение.Значение; // См. СвойстваВидаДоступа
		Строка.ВидДоступа = СвойстваВидаДоступа.Ссылка;
		
		Типы = Новый Массив;
		Типы.Добавить(КлючИЗначение.Ключ);
		ОписаниеТипа = Новый ОписаниеТипов(Типы);
		
		Строка.ТипЗначений = ОписаниеТипа.ПривестиЗначение(Неопределено);
	КонецЦикла;
	
	Возврат ТипыЗначенийВидовДоступа;
	
КонецФункции

// Смотри УправлениеДоступомПереопределяемый.ПриЗаполненииВозможныхПравДляНастройкиПравОбъектов.
// Смотри также заполнение в функции РегистрыСведений.НастройкиПравОбъектов.ВозможныеПраваСеанса.
//
// Возвращаемое значение:
//   Структура:
//     * ПоТипам - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип - тип ссылки или объекта из ОпределяемыйТип.ВладельцыНастроекПрав
//         ** Значение - ФиксированноеСоответствие из КлючИЗначение:
//             *** Ключ - Строка - имя возможного права
//             *** Значение - см. РегистрыСведений.НастройкиПравОбъектов.СвойстваВозможногоПрава
//     * ПоТипамСсылок - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип - тип ссылки из ОпределяемыйТип.ВладельцыНастроекПрав
//         ** Значение - ФиксированныйМассив из см. РегистрыСведений.НастройкиПравОбъектов.СвойстваВозможногоПрава
//     * ПоПолнымИменам - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Строка - владелец прав (полное имя таблицы)
//         ** Значение - см. РегистрыСведений.НастройкиПравОбъектов.СвойстваВозможногоПрава
//     * ТипыВладельцев - ФиксированныйМассив из ОпределяемыйТип.ВладелецНастроекПрав
//     * ОтдельныеТаблицы - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                - СправочникСсылка.ИдентификаторыОбъектовРасширений - идентификатор таблицы
//         ** Значение - Строка - полное имя таблицы
//     * ИерархическиеТаблицы - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Тип - тип ссылки или объекта из ОпределяемыйТип.ВладельцыНастроекПрав
//         ** Значение - Булево - Истина.
// 
Функция ВозможныеПраваДляНастройкиПравОбъектов() Экспорт
	
	Возврат РегистрыСведений.НастройкиПравОбъектов.ВозможныеПраваДляНастройкиПравОбъектов();
	
КонецФункции

// Смотри УправлениеДоступомПереопределяемый.ПриЗаполненииПоставляемыхПрофилейГруппДоступа.
// Смотри также заполнение в функции Справочники.ПрофилиГруппДоступа.ПроверенныеПоставляемыеПрофилиСеанса.
//
// Возвращаемое значение:
//   ФиксированнаяСтруктура:
//     * ОписанияПрофилей - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Строка
//         ** Значение - см. Справочники.ПрофилиГруппДоступа.СвойстваПоставляемогоПрофиля
//     * ОписанияПрофилейМассив - ФиксированныйМассив из см. Справочники.ПрофилиГруппДоступа.СвойстваПоставляемогоПрофиля
//     * ПапкиПоРодителям - ФиксированноеСоответствие из КлючИЗначение:
//         ** Ключ - Строка
//         ** Значение - ФиксированноеСоответствие из КлючИЗначение:
//              *** Ключ - Строка
//              *** Значение - Булево
//     * ПараметрыОбновления - ФиксированнаяСтруктура:
//         ** ОбновлятьИзмененныеПрофили - Булево
//         ** ЗапретитьИзменениеПрофилей - Булево
//         ** ОбновлятьГруппыДоступа - Булево
//         ** ОбновлятьГруппыДоступаСУстаревшимиНастройками - Булево
//
Функция ПоставляемыеПрофили() Экспорт
	
	Возврат Справочники.ПрофилиГруппДоступа.ПоставляемыеПрофили();
	
КонецФункции

// Смотри заполнение в функции Справочники.ПрофилиГруппДоступа.ПодготовленныеСтандартныеРолиРасширенийСеанса.
//
// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//   * ОбщиеПрава                       - ФиксированныйМассив из Строка - имена ролей <ПрефиксРасширения>ОбщиеПрава
//   * ПолныеПрава                      - ФиксированныйМассив из Строка - имена ролей <ПрефиксРасширения>ПолныеПрава
//   * БазовыеПрава                     - ФиксированныйМассив из Строка - имена ролей <ПрефиксРасширения>БазовыеПрава
//   * БазовыеПраваВнешнихПользователей - ФиксированныйМассив из Строка - имена ролей <ПрефиксРасширения>БазовыеПраваВнешнихПользователей
//   * АдминистраторСистемы             - ФиксированныйМассив из Строка - имена ролей <ПрефиксРасширения>АдминистраторСистемы
//   * Все                              - ФиксированноеСоответствие из КлючИЗначение:
//       ** Ключ     - Строка - имя роли расширений, из указанных выше.
//       ** Значение - Строка - имя вида ролей (имя свойства структуры).
//   * ДополнительныеРолиАдминистратора - ФиксированноеСоответствие из КлючИЗначение:
//       ** Ключ     - Строка - имя ролей расширений, которые можно назначить администратору.
//       ** Значение - Булево - Истина.
//
Функция СтандартныеРолиРасширений() Экспорт
	
	Возврат Справочники.ПрофилиГруппДоступа.СтандартныеРолиРасширений();
	
КонецФункции

#Область УниверсальноеОграничение

////////////////////////////////////////////////////////////////////////////////
// Обработчики подписок на события.

// Обработчик подписок ПроверитьДоступПередЗаписью* проверяет доступ к старой версии объекта, проверяет
// изменение полей объектов дополнительных таблиц, присоединенных в ограничениях доступа списков.
// Если изменения найдены, тогда регистрируется необходимость обновления ключей доступа
// для списков, у которых в ограничениях доступа присоединены дополнительные таблицы.
//
// Параметры:
//  Источник        - СправочникОбъект
//                  - ДокументОбъект
//                  - ПланВидовХарактеристикОбъект
//                  - ПланСчетовОбъект
//                  - ПланВидовРасчетаОбъект
//                  - БизнесПроцессОбъект
//                  - ЗадачаОбъект
//                  - ПланОбменаОбъект - объект данных, передаваемый в подписку на событие ПередЗаписью.
//
//  Отказ           - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//
//  РежимЗаписи     - РежимЗаписиДокумента - параметр, передаваемый в подписку на событие ПередЗаписью,
//                    когда Источник это ДокументОбъект.
//
//  РежимПроведения - РежимПроведенияДокумента - параметр, передаваемый в подписку на событие ПередЗаписью,
//                    когда Источник это ДокументОбъект.
//
Процедура ПроверитьДоступПередЗаписью(Источник, Отказ, РежимЗаписи = Неопределено, РежимПроведения = Неопределено) Экспорт
	
	// ОбменДанными.Загрузка обрабатывается внутри процедуры нестандартно (с учетом проверки прав).
	ПроверитьДоступПередЗаписьюИсточника(Источник, Отказ, Ложь, Ложь);
	
КонецПроцедуры

// Обработчик подписок ПроверитьДоступПередЗаписьюНабораЗаписей* проверяет доступ к старой версии набора записей,
// проверяет изменение полей наборов записей дополнительных таблиц, присоединенных в ограничениях доступа списков.
// Если изменения найдены, тогда регистрируется необходимость обновления ключей доступа
// для списков, у которых в ограничениях доступа присоединены дополнительные таблицы.
//
// Параметры:
//  Источник        - РегистрСведенийНаборЗаписей
//                  - РегистрНакопленияНаборЗаписей
//                  - РегистрБухгалтерииНаборЗаписей
//                  - РегистрРасчетаНаборЗаписей
//                  - ПерерасчетНаборЗаписей - набор записей, передаваемый в подписку
//                                             на событие ПередЗаписью.
//
//  Отказ           - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//  Замещение       - Булево - параметр, передаваемый в подписку на событие ПередЗаписью.
//
//  ТолькоЗапись    - Булево - параметр, передаваемый в подписку на событие ПередЗаписью,
//                    когда Источник это РегистрРасчетаНаборЗаписей.
//
//  ЗаписьФактическогоПериодаДействия - Булево - параметр, передаваемый в подписку на событие ПередЗаписью,
//                    когда Источник это РегистрРасчетаНаборЗаписей.
//
//  ЗаписьПерерасчетов - Булево - параметр, передаваемый в подписку на событие ПередЗаписью,
//                    когда Источник это РегистрРасчетаНаборЗаписей.
//
Процедура ПроверитьДоступПередЗаписьюНабораЗаписей(Источник, Отказ, Замещение,
				ТолькоЗапись = Неопределено,
				ЗаписьФактическогоПериодаДействия = Неопределено,
				ЗаписьПерерасчетов = Неопределено) Экспорт
	
	// ОбменДанными.Загрузка обрабатывается внутри процедуры нестандартно (с учетом проверки прав).
	ПроверитьДоступПередЗаписьюИсточника(Источник, Отказ, Истина, Замещение);
	
КонецПроцедуры

// Обработчик подписок ПроверитьДоступПриЗаписи* проверяет устаревание ключа доступа
// новой версии объекта. Обновляет устаревший ключ доступа и, в этом случае,
// выполняет проверку прав Чтение и Изменение новой версии объекта.
//
// Параметры:
//  Источник        - СправочникОбъект
//                  - ДокументОбъект
//                  - ПланВидовХарактеристикОбъект
//                  - ПланСчетовОбъект
//                  - ПланВидовРасчетаОбъект
//                  - БизнесПроцессОбъект
//                  - ЗадачаОбъект
//                  - ПланОбменаОбъект - объект данных, передаваемый в подписку на событие ПриЗаписи.
//
//  Отказ           - Булево - параметр, передаваемый в подписку на событие ПриЗаписи.
//
Процедура ПроверитьДоступПриЗаписи(Источник, Отказ) Экспорт
	
	// ОбменДанными.Загрузка обрабатывается внутри процедуры нестандартно (с учетом проверки прав).
	ПроверитьДоступПриЗаписиИсточника(Источник, Отказ, Ложь, Ложь);
	
КонецПроцедуры

// Обработчик подписок ПроверитьДоступПриЗаписиНабораЗаписей* проверяет устаревание ключей доступа
// новой версии набора записей. Обновляет устаревшие ключи доступа и, в этом случае,
// выполняет проверку прав Чтение и Изменение новой версии набора записей.
//
// Параметры:
//  Источник        - РегистрСведенийНаборЗаписей
//                  - РегистрНакопленияНаборЗаписей
//                  - РегистрБухгалтерииНаборЗаписей
//                  - РегистрРасчетаНаборЗаписей
//                  - ПерерасчетНаборЗаписей - набор записей, передаваемый в подписку
//                                             на событие ПриЗаписи.
//
//  Отказ           - Булево - параметр, передаваемый в подписку на событие ПриЗаписи.
//  Замещение       - Булево - параметр, передаваемый в подписку на событие ПриЗаписи.
//
//  ТолькоЗапись    - Булево - параметр, передаваемый в подписку на событие ПриЗаписи,
//                    когда Источник это РегистрРасчетаНаборЗаписей.
//
//  ЗаписьФактическогоПериодаДействия - Булево - параметр, передаваемый в подписку на событие ПриЗаписи,
//                    когда Источник это РегистрРасчетаНаборЗаписей.
//
//  ЗаписьПерерасчетов - Булево - параметр, передаваемый в подписку на событие ПриЗаписи,
//                    когда Источник это РегистрРасчетаНаборЗаписей.
//
Процедура ПроверитьДоступПриЗаписиНабораЗаписей(Источник, Отказ, Замещение,
				ТолькоЗапись = Неопределено,
				ЗаписьФактическогоПериодаДействия = Неопределено,
				ЗаписьПерерасчетов = Неопределено) Экспорт
	
	// ОбменДанными.Загрузка обрабатывается внутри процедуры нестандартно (с учетом проверки прав).
	ПроверитьДоступПриЗаписиИсточника(Источник, Отказ, Истина, Замещение);
	
КонецПроцедуры

// Обработчик подписок ПроверитьДоступПередУдалением* проверяет
// изменение полей объектов дополнительных таблиц, присоединенных в ограничениях доступа списков.
// Если изменения найдены, тогда регистрируется необходимость обновления ключей доступа
// для списков, у которых в ограничениях доступа присоединены дополнительные таблицы.
//
// Параметры:
//  Источник - СправочникОбъект
//           - ДокументОбъект
//           - ПланВидовХарактеристикОбъект
//           - ПланСчетовОбъект
//           - ПланВидовРасчетаОбъект
//           - БизнесПроцессОбъект
//           - ЗадачаОбъект
//           - ПланОбменаОбъект - объект данных, передаваемый в подписку на событие ПередУдалением.
//
//  Отказ    - Булево - параметр, передаваемый в подписку на событие ПередУдалением.
//
Процедура ПроверитьДоступПередУдалением(Источник, Отказ) Экспорт
	
	// ОбменДанными.Загрузка обрабатывается внутри процедуры нестандартно (с учетом проверки прав).
	ПроверитьДоступПередУдалениемИсточника(Источник, Отказ);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Универсальное ограничение доступа.

// Параметры:
//  ОписаниеДанных - см. УправлениеДоступом.ИзменениеРазрешено.ОписаниеДанных
//  ПравоИзменение - Булево
//  ВызыватьИсключение - Булево
//  ПроверитьТолькоСтаруюВерсию - Булево
//  Пользователь   - СправочникСсылка.Пользователи
//                 - СправочникСсылка.ВнешниеПользователи
//                 - Неопределено - проверить для текущего пользователя.
//
// Возвращаемое значение:
//  Булево
//
Функция ДоступРазрешен(ОписаниеДанных, ПравоИзменение, ВызыватьИсключение = Ложь,
			ПроверитьТолькоСтаруюВерсию = Ложь, Знач Пользователь = Неопределено) Экспорт
	
	УчитыватьПривилегированныйРежим = Истина;
	ПользовательИБ = Неопределено;
	
	Если Пользователь <> Неопределено
	   И Пользователь = Пользователи.АвторизованныйПользователь() Тогда
		
		Пользователь = Неопределено;
		УчитыватьПривилегированныйРежим = Ложь;
		ПользовательИБ = ПользователиИнформационнойБазы.ТекущийПользователь();
	КонецЕсли;
	
	Если Пользователь = Неопределено
	   И Пользователи.ЭтоПолноправныйПользователь(,, УчитыватьПривилегированныйРежим) Тогда
		Возврат Истина;
	КонецЕсли;
	
	ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(ОписаниеДанных));
	Если ОбъектМетаданных = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недопустимое значение параметра %1 в %2.
			           |Ожидалась ссылка, объект, ключ записи или набор записей.
			           |Передано значение: %3 (тип %4).'"),
			"ОписаниеДанных",
			?(ВызыватьИсключение,
				?(ПравоИзменение, "УправлениеДоступом.ПроверитьИзменениеРазрешено",
					"УправлениеДоступом.ПроверитьЧтениеРазрешено"),
				?(ПравоИзменение, "УправлениеДоступом.ИзменениеРазрешено",
					"УправлениеДоступом.ЧтениеРазрешено")),
			Строка(ОписаниеДанных),
			Строка(ТипЗнч(ОписаниеДанных)));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ПроизводительныйВариант = УправлениеДоступом.ПроизводительныйВариант();
	
	Если Пользователь <> Неопределено Тогда
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(
			?(ВызыватьИсключение,
				?(ПравоИзменение, "УправлениеДоступом.ПроверитьИзменениеРазрешено",
					"УправлениеДоступом.ПроверитьЧтениеРазрешено"),
				?(ПравоИзменение, "УправлениеДоступом.ИзменениеРазрешено",
					"УправлениеДоступом.ЧтениеРазрешено")),
				"Пользователь",
				Пользователь,
				Новый ОписаниеТипов("СправочникСсылка.Пользователи, СправочникСсылка.ВнешниеПользователи"));
		
		Если Не ПроизводительныйВариант Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Недопустимое значение параметра %1 в %2.
				           |В стандартном варианте ограничения доступа поддерживается только %3.
				           |Передано значение: %4 (тип %5).'"),
				"Пользователь",
				?(ВызыватьИсключение,
					?(ПравоИзменение, "УправлениеДоступом.ПроверитьИзменениеРазрешено",
						"УправлениеДоступом.ПроверитьЧтениеРазрешено"),
					?(ПравоИзменение, "УправлениеДоступом.ИзменениеРазрешено",
						"УправлениеДоступом.ЧтениеРазрешено")),
				"Неопределено",
				Строка(Пользователь),
				Строка(ТипЗнч(Пользователь)));
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Если ВызыватьИсключение Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Вызов исключения поддерживается только для текущего пользователя.
				           |В %1
				           |- либо недопустимое значение параметра %2
				           |передано значение: %3 (тип %4),
				           |- либо недопустимое значение параметра %5
				           |передано значение: %6 (тип %7).'"),
				"УправлениеДоступом.ДоступРазрешен",
				"ВызыватьИсключение",
				Строка(ВызыватьИсключение),
				Строка(ТипЗнч(ВызыватьИсключение)),
				"Пользователь",
				Строка(Пользователь),
				Строка(ТипЗнч(Пользователь)));
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		
		Если Пользователи.ЭтоПолноправныйПользователь(Пользователь,, УчитыватьПривилегированныйРежим) Тогда
			Возврат Истина;
		КонецЕсли;
		
		ИдентификаторПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Пользователь,
			"ИдентификаторПользователяИБ");
		
		Если ТипЗнч(ИдентификаторПользователяИБ) <> Тип("УникальныйИдентификатор") Тогда
			Возврат Ложь;
		КонецЕсли;
		
		ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(
			ИдентификаторПользователяИБ);
		
		Если ПользовательИБ = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	ЛюбойОбъект = ОписаниеДанных; // СправочникОбъект
	
	Если Не ПравоИзменение Тогда
		ИмяПраваДоступа = "Чтение";
		
	ИначеЕсли ОбщегоНазначения.ЭтоОбъектСсылочногоТипа(ОбъектМетаданных)
	        И Не ОбщегоНазначения.ЭтоСсылка(ТипЗнч(ЛюбойОбъект))
	        И ЛюбойОбъект.ЭтоНовый() Тогда
		
		ИмяПраваДоступа = "Добавление";
	Иначе
		ИмяПраваДоступа = "Изменение";
	КонецЕсли;
	
	ЕстьПраво = Истина;
	Если ВызыватьИсключение Тогда
		ВыполнитьПроверкуПравДоступа(ИмяПраваДоступа, ОбъектМетаданных);
		
	ИначеЕсли ПользовательИБ = Неопределено Тогда
		ЕстьПраво = ПравоДоступа(ИмяПраваДоступа, ОбъектМетаданных);
		
	ИначеЕсли Пользователь = Неопределено Тогда
		УстановитьОтключениеБезопасногоРежима(Истина);
		УстановитьПривилегированныйРежим(Истина);
		
		ЕстьПраво = ПравоДоступа(ИмяПраваДоступа, ОбъектМетаданных, ПользовательИБ);
		
		УстановитьПривилегированныйРежим(Ложь);
		УстановитьОтключениеБезопасногоРежима(Ложь);
	Иначе
		ЕстьПраво = ПравоДоступа(ИмяПраваДоступа, ОбъектМетаданных, ПользовательИБ);
	КонецЕсли;
	Если Не ЕстьПраво Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если УправлениеДоступомСлужебныйПовтИсп.ЭтоПользовательБезОграниченияДоступа(Пользователь) Тогда
		Возврат Истина;
	КонецЕсли;
	
	ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
	
	Если Не ПроизводительныйВариант Тогда
		Возврат ЧтениеДоступно(ПолноеИмя, ОбъектМетаданных, ОписаниеДанных, ВызыватьИсключение);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Пользователь) Тогда
		ДляВнешнихПользователей = ТипЗнч(Пользователь) = Тип("СправочникСсылка.ВнешниеПользователи");
	Иначе
		ДляВнешнихПользователей = Неопределено;
	КонецЕсли;
	ИдентификаторТранзакции = Новый УникальныйИдентификатор;
	ПараметрыОграничения = ПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции, ДляВнешнихПользователей);
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ОбновитьРазрешенныеНаборыВПараметрахСеанса();
	
	Если ПараметрыОграничения.ОграничениеОтключено
	 Или Не ПравоИзменение
	   И ПараметрыОграничения.ОграничениеЧтенияОтключено Тогда
		
		Возврат Истина;
	КонецЕсли;
	
	СтараяВерсия = Истина;
	ПравоИзменения = Ложь;
	ЭтоНовый = Ложь;
	
	Если ПараметрыОграничения.ДоступЗапрещен Тогда
		ДоступРазрешен = Ложь;
	Иначе
		Запрос = Новый Запрос;
		УстановитьРазрешенныеНаборыВПараметрыЗапроса(Запрос, Пользователь);
		
		Запрос.Текст = ?(ПравоИзменение, ПараметрыОграничения.ТекстЗапросаПроверкиПравЧтениеИзменение,
			ПараметрыОграничения.ТекстЗапросаПроверкиПраваЧтение);
		
		Если ПараметрыОграничения.ЭтоСсылочныйТип Тогда
			СсылкаНаОбъект = ?(ОбщегоНазначения.ЭтоСсылка(ТипЗнч(ОписаниеДанных)),
				ОписаниеДанных, ОписаниеДанных.Ссылка);
			Запрос.УстановитьПараметр("Объект", СсылкаНаОбъект);
			Если ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
				Запрос.УстановитьПараметр("ИдентификаторТаблицыНастроекПрав",
					ПараметрыОграничения.ИдентификаторТаблицыНастроекПрав);
			КонецЕсли;
			Если ОписаниеДанных = СсылкаНаОбъект Или ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
				ДоступРазрешен = Не Запрос.Выполнить().Пустой();
			Иначе
				ДоступРазрешен = Истина;
			КонецЕсли;
			
			Если Не ДоступРазрешен Тогда
				ДанныеДляПредставления = СсылкаНаОбъект;
				
			ИначеЕсли ОписаниеДанных <> СсылкаНаОбъект И Не ПроверитьТолькоСтаруюВерсию Тогда
				СтараяВерсия = Ложь;
				Если ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
					ПоляЧерезТочку = СтрРазделить(ПараметрыОграничения.ПолеВладельца.Имя, ".", Ложь);
					ПолеОбъекта = ПоляЧерезТочку[0];
					ЗначениеПоляОбъекта = ОписаниеДанных[ПолеОбъекта];
					Если ПоляЧерезТочку.Количество() = 1 Тогда
						ПараметрИзПамяти = "&Владелец";
					Иначе
						ОбъектМетаданныхПоТипу = Метаданные.НайтиПоТипу(ТипЗнч(ЗначениеПоляОбъекта));
						Если ОбъектМетаданныхПоТипу = Неопределено Тогда
							ПараметрИзПамяти = "&Владелец";
							ЗначениеПоляОбъекта = Null;
						Иначе
							ПоляЧерезТочку.Удалить(0);
							ПараметрИзПамяти = "ВЫРАЗИТЬ(&Владелец КАК " + ОбъектМетаданныхПоТипу.ПолноеИмя() // @query-part-1
								+ ")." + СтрСоединить(ПоляЧерезТочку, ".");
						КонецЕсли;
					КонецЕсли;
					Запрос.Текст = СтрЗаменить(Запрос.Текст,
						ПараметрыОграничения.ПолеОбъектаВладельцаВЗапросеПроверкиПрав, ПараметрИзПамяти);
					Запрос.УстановитьПараметр("Владелец", ЗначениеПоляОбъекта);
				Иначе
					МодельОбъектовВПамяти = МодельОбъектовВПамяти(ОписаниеДанных, ПараметрыОграничения);
					ПараметрыОбновления = Новый Структура(ПараметрыОграничения);
					ПараметрыОбновления.Вставить("МодельОбъектовВПамяти", МодельОбъектовВПамяти);
					ПараметрыОбновления.Вставить("ИдентификаторТранзакции", ИдентификаторТранзакции);
					ПараметрыОбновления.Вставить("ИдентификаторСписка",
						ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ПараметрыОграничения.Список));
					ОбновитьКлючиДоступаПорцииЭлементовДанныхСписка(МодельОбъектовВПамяти.ЭлементыДанных, ПараметрыОбновления);
					Запрос.УстановитьПараметр("КлючиДоступаКОбъектам", МодельОбъектовВПамяти.КлючиДоступаКОбъектам);
					Запрос.УстановитьПараметр("Объект", МодельОбъектовВПамяти.ЭлементыДанных[0].ТекущаяСсылка);
					ТекстЗапроса =
					"ВЫБРАТЬ
					|	КлючиДоступаКОбъектам.Объект КАК Объект,
					|	КлючиДоступаКОбъектам.КлючДоступаПользователей КАК КлючДоступаПользователей,
					|	КлючиДоступаКОбъектам.КлючДоступаВнешнихПользователей КАК КлючДоступаВнешнихПользователей
					|ПОМЕСТИТЬ РегистрСведений_КлючиДоступаКОбъектам
					|ИЗ
					|	&КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам";
					Запрос.Текст = СтрЗаменить(Запрос.Текст, "РегистрСведений.КлючиДоступаКОбъектам",
						"РегистрСведений_КлючиДоступаКОбъектам");
					Запрос.Текст = ТекстЗапроса + ОбщегоНазначения.РазделительПакетаЗапросов() + Запрос.Текст;
				КонецЕсли;
				ПроверкаДобавления = Ложь;
				ТекстЗапросаИзменения = Запрос.Текст;
				ЭтоНовый = ОписаниеДанных.ЭтоНовый();
				УточнитьПравоДобавление(Запрос, ЭтоНовый, ПараметрыОграничения, ПроверкаДобавления);
				ДоступРазрешен = Не Запрос.Выполнить().Пустой();
				Если Не ДоступРазрешен И ПроверкаДобавления Тогда
					Запрос.Текст = ТекстЗапросаИзменения;
					ПравоИзменения = Не Запрос.Выполнить().Пустой();
				КонецЕсли;
				ДанныеДляПредставления = ОписаниеДанных;
			КонецЕсли;
		Иначе
			НаборЗаписей = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя).СоздатьНаборЗаписей();
			
			Если ТипЗнч(ОписаниеДанных) = ТипЗнч(НаборЗаписей) Тогда
				НаборЗаписей = ОписаниеДанных;
			Иначе // КлючЗаписи.
				Для Каждого ЭлементОтбора Из НаборЗаписей.Отбор Цикл
					ЭлементОтбора.Значение = ОписаниеДанных[ЭлементОтбора.Имя];
					ЭлементОтбора.Использование = Истина;
				КонецЦикла;
			КонецЕсли;
			
			ОтборПоИзмерениям = ОтборПоИзмерениямНабораЗаписей(НаборЗаписей, Запрос);
			ТекстЗапроса = Запрос.Текст;
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОтборПоИзмерениям", ОтборПоИзмерениям);
			ДоступРазрешен = Запрос.Выполнить().Пустой();
			
			Если ДоступРазрешен
			   И ТипЗнч(ОписаниеДанных) = ТипЗнч(НаборЗаписей)
			   И Не ПроверитьТолькоСтаруюВерсию Тогда
				
				СтараяВерсия = Ложь;
				Если ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
					ПолеВладельца = ПараметрыОграничения.ПолеВладельца.Имя;
					Комбинации = ОписаниеДанных.Выгрузить(, ПолеВладельца); // ТаблицаЗначений
					Комбинации.Свернуть(ПолеВладельца);
					Запрос.УстановитьПараметр("КомбинацииЗначенийОпорныхПолей", Комбинации);
					ТекстЗапросаПодготовкиКомбинаций = // @query-part-1
					"ВЫБРАТЬ ТекущаяТаблица." + ПолеВладельца + "
					|ПОМЕСТИТЬ КомбинацииЗначенийОпорныхПолей
					|ИЗ &КомбинацииЗначенийОпорныхПолей КАК ТекущаяТаблица"; // @query-part-1
				Иначе
					ТекстЗапросаПодготовкиКомбинаций = // @query-part-1
					"ВЫБРАТЬ ТекущаяТаблица." + СтрСоединить(ПараметрыОграничения.ОпорныеПоля.Используемые, ",
					|	ТекущаяТаблица.") + "
					|ПОМЕСТИТЬ КомбинацииЗначенийОпорныхПолей
					|ИЗ &КомбинацииЗначенийОпорныхПолей КАК ТекущаяТаблица"; // @query-part-1
					ОписаниеДанных.ДополнительныеСвойства.Вставить("УправлениеДоступомИдентификаторТранзакции",
						ИдентификаторТранзакции);
					ОбновитьКлючиДоступаКНаборуЗаписей(ОписаниеДанных, Ложь, Ложь,
						ИдентификаторТранзакции, ПараметрыОграничения, "", Запрос);
					ОписаниеДанных.ДополнительныеСвойства.Удалить("УправлениеДоступомИдентификаторТранзакции");
				КонецЕсли;
				Запрос.Текст = СтрЗаменить(ТекстЗапроса, "&ОтборПоИзмерениям", "ИСТИНА");
				ПолноеИмяРегистра = Метаданные.НайтиПоТипу(ТипЗнч(ОписаниеДанных)).ПолноеИмя();
				Запрос.Текст = ТекстЗапросаПодготовкиКомбинаций + ОбщегоНазначения.РазделительПакетаЗапросов()
					+ СтрЗаменить(Запрос.Текст, ПолноеИмяРегистра + " КАК ТекущаяТаблица", // @query-part-1 @query-part-2
						"КомбинацииЗначенийОпорныхПолей КАК ТекущаяТаблица");
				
				ДоступРазрешен = Запрос.Выполнить().Пустой();
			КонецЕсли;
			ДанныеДляПредставления = НаборЗаписей;
		КонецЕсли;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
	Если ДоступРазрешен Или Не ВызыватьИсключение Тогда
		Возврат ДоступРазрешен;
	КонецЕсли;
	
	Если ПравоИзменение Тогда
		ПравоЧтения = ДоступРазрешен(ОписаниеДанных, Ложь);
	Иначе
		ПравоЧтения = Ложь;
	КонецЕсли;
	
	СообщитьОбОшибкеДоступа(ДанныеДляПредставления, СтараяВерсия, ПравоЧтения, ПравоИзменения, ЭтоНовый);
	
	Возврат Ложь;
	
КонецФункции

// Для функции ДоступРазрешен.
Функция ЧтениеДоступно(ПолноеИмя, ОбъектМетаданных, ОписаниеДанных, ВызыватьИсключение)
	
	Запрос = Новый Запрос;
	
	Если ЭтоСсылочныйТипТаблицы(ПолноеИмя) Тогда
		СсылкаНаОбъект = ?(ОбщегоНазначения.ЭтоСсылка(ТипЗнч(ОписаниеДанных)),
			ОписаниеДанных, ОписаниеДанных.Ссылка);
		
		Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
		ТекстЗапроса =
		"ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1
		|	ИСТИНА КАК ЗначениеИстина
		|ИЗ
		|	&ТекущаяТаблица КАК ТекущаяТаблица
		|ГДЕ
		|	ТекущаяТаблица.Ссылка = &Ссылка";
		Запрос.Текст = СтрЗаменить(ТекстЗапроса, "&ТекущаяТаблица", ПолноеИмя);
		Если Не Запрос.Выполнить().Пустой() Тогда
			Возврат Истина;
		КонецЕсли;
		ДанныеДляПредставления = СсылкаНаОбъект;
	Иначе
		НаборЗаписей = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя).СоздатьНаборЗаписей();
		
		Если ТипЗнч(ОписаниеДанных) = ТипЗнч(НаборЗаписей) Тогда
			НаборЗаписей = ОписаниеДанных;
		Иначе // КлючЗаписи.
			Для Каждого ЭлементОтбора Из НаборЗаписей.Отбор Цикл
				ЭлементОтбора.Значение = ОписаниеДанных[ЭлементОтбора.Имя];
				ЭлементОтбора.Использование = Истина;
			КонецЦикла;
		КонецЕсли;
		
		ОтборПоИзмерениям = ОтборПоИзмерениямНабораЗаписей(НаборЗаписей, Запрос);
		ТекстЗапроса =
		"ВЫБРАТЬ РАЗРЕШЕННЫЕ
		|	КОЛИЧЕСТВО(1) КАК КоличествоДанных
		|ИЗ
		|	&ТекущаяТаблица КАК ТекущаяТаблица
		|ГДЕ
		|	&ОтборПоИзмерениям";
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОтборПоИзмерениям", ОтборПоИзмерениям);
		Запрос.Текст = СтрЗаменить(ТекстЗапроса, "&ТекущаяТаблица", ПолноеИмя);
		
		УстановитьПривилегированныйРежим(Истина);
		Выборка = Запрос.Выполнить().Выбрать();
		УстановитьПривилегированныйРежим(Ложь);
		ВсегоДанных = ?(Выборка.Следующий(), Выборка.КоличествоДанных, 0);
		
		Выборка = Запрос.Выполнить().Выбрать();
		ДоступноДанных = ?(Выборка.Следующий(), Выборка.КоличествоДанных, 0);
		
		Если ВсегоДанных = ДоступноДанных Тогда
			Возврат Истина;
		КонецЕсли;
		ДанныеДляПредставления = НаборЗаписей;
	КонецЕсли;
	
	Если Не ВызыватьИсключение Тогда
		Возврат Ложь;
	КонецЕсли;
	
	СообщитьОбОшибкеДоступа(ДанныеДляПредставления, Ложь, Ложь, Ложь, Ложь);
	
	Возврат Ложь;
	
КонецФункции

// Требуется, как начальная максимальная дата при планировании начального обновления доступа.
//
// Возвращаемое значение:
//  Дата
//
Функция МаксимальнаяДата() Экспорт
	
	Возврат '39991231235959';
	
КонецФункции

// Требуется, как начальная максимальная дата при планировании продолжения обновления доступа.
Функция МаксимальнаяДатаПриПродолжении()
	
	Возврат '39990101000000';
	
КонецФункции

// Для функции НастройкиВнедрения.
Функция ПолноеИмяXML(ПолноеИмя, ТипыТаблицПоИменам)
	
	СоставИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
	Свойства = ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
	
	Возврат Свойства.ЯзыкАнглийский + "." + СоставИмени[1];
	
КонецФункции

// Для процедуры ДобавитьТипыТребуемыеВОпределяемомТипе.
Функция ИмяТипаСсылки(ПолноеИмя, ТипыТаблицПоИменам)
	
	СоставИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
	Свойства = ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
	
	Если Не Свойства.ЭтоСсылочныйТип Тогда
		Возврат "";
	КонецЕсли;
	
	Если ВРег(Свойства.ЯзыкРусский) = ВРег(СоставИмени[0]) Тогда
		ИмяТипа = Свойства.ЯзыкРусский + "Ссылка." + СоставИмени[1]; // @Non-NLS
	Иначе
		ИмяТипа = Свойства.ЯзыкАнглийский + "Ref." + СоставИмени[1];
	КонецЕсли;
	
	Возврат ИмяТипа;
	
КонецФункции

// Для функции НастройкиВнедрения.
Функция ИмяТипаСсылкиXML(ПолноеИмя, ТипыТаблицПоИменам)
	
	СоставИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
	Свойства = ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
	
	Если Свойства.ЭтоСсылочныйТип Тогда
		Возврат Свойства.ЯзыкАнглийский + "Ref." + СоставИмени[1];
	КонецЕсли;
	
	Возврат "";
	
КонецФункции

// Для функции ДобавитьТипыТребуемыеВОпределяемомТипе.
Функция ИмяТипаОбъектаИлиНабораЗаписей(ПолноеИмя, ТипыТаблицПоИменам)
	
	СоставИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
	Свойства = ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
	ИмяТипа = "";
	
	Если Свойства.ЭтоСсылочныйТип Тогда
		Если ВРег(Свойства.ЯзыкРусский) = ВРег(СоставИмени[0]) Тогда
			ИмяТипа = Свойства.ЯзыкРусский + "Объект." + СоставИмени[1]; // @Non-NLS
		Иначе
			ИмяТипа = Свойства.ЯзыкАнглийский + "Object." + СоставИмени[1];
		КонецЕсли;
	КонецЕсли;
	
	Если СтрНачинаетсяС(Свойства.ИмяКоллекции, "Регистры") Тогда
		Если ВРег(Свойства.ЯзыкРусский) = ВРег(СоставИмени[0]) Тогда
			ИмяТипа = Свойства.ЯзыкРусский + "НаборЗаписей." + СоставИмени[1]; // @Non-NLS
		Иначе
			ИмяТипа = Свойства.ЯзыкАнглийский + "RecordSet." + СоставИмени[1];
		КонецЕсли;
	КонецЕсли;
	
	Возврат ИмяТипа;
	
КонецФункции

// Для функции НастройкиВнедрения.
Функция ИмяТипаОбъектаИлиНабораЗаписейXML(ПолноеИмя, ТипыТаблицПоИменам)
	
	СоставИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
	Свойства = ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
	
	Если Свойства.ЭтоСсылочныйТип Тогда
		Возврат Свойства.ЯзыкАнглийский + "Object." + СоставИмени[1];
	КонецЕсли;
	
	Если СтрНачинаетсяС(Свойства.ИмяКоллекции, "Регистры") Тогда
		Возврат Свойства.ЯзыкАнглийский + "RecordSet." + СоставИмени[1];
	КонецЕсли;
	
	Возврат "";
	
КонецФункции

// Для функции НастройкиВнедрения.
Процедура ДобавитьОбъектКВладельцам(ИмяТипаОбъектаXML, ВладельцыЗначенийКлючейДоступа)
	
	Если СтрНачинаетсяС(ИмяТипаОбъектаXML, "DocumentObject.") Тогда
		ВладельцыЗначенийКлючейДоступа.Документы.Добавить(ИмяТипаОбъектаXML);
		
	ИначеЕсли СтрНачинаетсяС(ИмяТипаОбъектаXML, "CalculationRegisterRecordSet.") Тогда
		ВладельцыЗначенийКлючейДоступа.НаборыЗаписейРегистраРасчета.Добавить(ИмяТипаОбъектаXML);
		
	ИначеЕсли СтрНайти(ИмяТипаОбъектаXML, "RecordSet.") > 0 Тогда
		ВладельцыЗначенийКлючейДоступа.НаборыЗаписей.Добавить(ИмяТипаОбъектаXML);
		
	ИначеЕсли СтрНайти(ИмяТипаОбъектаXML, "Object.") > 0 Тогда
		ВладельцыЗначенийКлючейДоступа.Объекты.Добавить(ИмяТипаОбъектаXML);
	КонецЕсли;
	
КонецПроцедуры

// Для функции НастройкиВнедрения.
Процедура ДобавитьОграниченияВРолях(ПолноеИмяXML, ПолноеИмя, ОграниченияВРолях,
			СвойстваОграничений, ТипыИзмеренийРегистровКлючей, ТипыТаблицПоИменам, ПредопределенныеИдентификаторы)
	
	Свойства = СвойстваОграничений.Получить(ПолноеИмя);
	Если Свойства <> Неопределено И Свойства.ДоступЗапрещен Тогда
		Возврат;
	КонецЕсли;
	
	ОграничениеВРоли = Новый Структура("ШаблонДляОбъекта, Параметры", Истина, Новый Массив);
	ОграниченияВРолях.Вставить(ПолноеИмяXML, ОграничениеВРоли);
	
	Если Свойства = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если Свойства.ПолеВладельца <> Неопределено Тогда
		ПолеВладельца = Свойства.ПолеВладельца; // См. НовоеПолеВладельца
		ОграничениеВРоли.Параметры.Добавить(ПолеВладельца.Имя);
		Возврат;
	КонецЕсли;
	
	Если Свойства.ОпорныеПоля = Неопределено
	 Или Не ЗначениеЗаполнено(Свойства.ОпорныеПоля) Тогда
		Возврат;
	КонецЕсли;
	
	ОграничениеВРоли.ШаблонДляОбъекта = Ложь;
	
	Если ЗначениеЗаполнено(Свойства.ИмяОтдельногоРегистраКлючей) Тогда
		ПервыйПараметр = Свойства.ИмяОтдельногоРегистраКлючей;
		
		ДобавитьТипыИзмерения(Свойства.ИмяОтдельногоРегистраКлючей,
			Свойства.ОпорныеПоля, ТипыИзмеренийРегистровКлючей, ТипыТаблицПоИменам, ПолноеИмя);
	Иначе
		ПервыйПараметр =
			УправлениеДоступомСлужебныйПовтИсп.ОписаниеПредопределенногоИдентификатораОбъектаМетаданных(ПолноеИмя);
		
		ДобавитьТипыИзмерения("КлючиДоступаКРегистрам",
			Свойства.ОпорныеПоля, ТипыИзмеренийРегистровКлючей, ТипыТаблицПоИменам, ПолноеИмя);
		
		ПредопределенныеИдентификаторы.Вставить(ПервыйПараметр, ПолноеИмя);
	КонецЕсли;
	
	ОграничениеВРоли.Параметры.Добавить(ПервыйПараметр);
	
	Для Каждого ОпорноеПоле Из Свойства.ОпорныеПоля.Все Цикл
		ОграничениеВРоли.Параметры.Добавить(ОпорноеПоле);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьОграниченияВРолях.
Процедура ДобавитьТипыИзмерения(ИмяРегистраКлючей, ОпорныеПоля, ТипыИзмеренийРегистровКлючей,
			ТипыТаблицПоИменам, ИмяИсходногоРегистра)
	
	Если ОпорныеПоля.Все.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ТипыИзмерений = ТипыИзмеренийРегистраКлючей(ТипыИзмеренийРегистровКлючей, ИмяРегистраКлючей);
	
	ИменаТипов           = ТипыИзмерений.ИменаТипов;
	ПоляРегистровПоТипам = ТипыИзмерений.ПоляРегистровПоТипам;
	
	ПоляРегистра = Новый Массив;
	ТипыИзмерений.ПоляРегистров.Вставить(ИмяИсходногоРегистра, ПоляРегистра);
	
	Для Каждого ИмяПоля Из ОпорныеПоля.Все Цикл
		ХранилищеТиповПоля = ОпорныеПоля.ТипыВсех[ОпорныеПоля.Все.Найти(ИмяПоля)]; // ХранилищеЗначения
		ТипыПоля = ХранилищеТиповПоля.Получить();
		ПоляРегистра.Добавить(Новый Структура("Поле, Тип", ИмяПоля, ТипыПоля));
		Для Каждого Тип Из ТипыПоля.Типы() Цикл
			ОбъектМетаданных = Метаданные.НайтиПоТипу(Тип);
			Если ОбъектМетаданных = Неопределено Тогда
				ИмяТипа = XMLТип(Тип).ИмяТипа;
			Иначе
				ИмяТипа = ИмяТипаСсылкиXML(Метаданные.НайтиПоТипу(Тип).ПолноеИмя(), ТипыТаблицПоИменам);
			КонецЕсли;
			Если ИменаТипов.Найти(ИмяТипа) = Неопределено Тогда
				ИменаТипов.Добавить(ИмяТипа);
			КонецЕсли;
			ПоляРегистровПоТипу = ПоляРегистровПоТипам.Получить(ИмяТипа);
			Если ПоляРегистровПоТипу = Неопределено Тогда
				ПоляРегистровПоТипу = Новый Массив;
				ПоляРегистровПоТипам.Вставить(ИмяТипа, ПоляРегистровПоТипу);
			КонецЕсли;
			ПолноеИмяПоля = ИмяИсходногоРегистра + "." + ИмяПоля;
			Если ПоляРегистровПоТипу.Найти(ПолноеИмяПоля) = Неопределено Тогда
				ПоляРегистровПоТипу.Добавить(ПолноеИмяПоля);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для функции НастройкиВнедрения и процедуры ДобавитьТипыИзмерения.
Функция ТипыИзмеренийРегистраКлючей(ТипыИзмеренийРегистровКлючей, ИмяРегистраКлючей)
	
	ТипыИзмерений = ТипыИзмеренийРегистровКлючей.Получить(ИмяРегистраКлючей);
	Если ТипыИзмерений = Неопределено Тогда
		ТипыИзмерений = Новый Структура;
		ТипыИзмерений.Вставить("ИменаТипов", Новый Массив);
		ТипыИзмерений.Вставить("ПоляРегистров", Новый Соответствие);
		ТипыИзмерений.Вставить("ПоляРегистровПоТипам", Новый Соответствие);
		ТипыИзмеренийРегистровКлючей.Вставить(ИмяРегистраКлючей, ТипыИзмерений);
		ТипыИзмерений.ИменаТипов.Добавить("EnumRef.ДополнительныеЗначенияДоступа");
	КонецЕсли;
	
	Возврат ТипыИзмерений;
	
КонецФункции

// Для функции ОграничиватьДоступНаУровнеЗаписейУниверсально.
Функция КонстантаПервоеОбновлениеДоступаЗавершилось()
	
	Возврат Не ВариантВстроенногоЯзыкаРусский()
		Или Константы.ПервоеОбновлениеДоступаЗавершилось.Получить();
	
КонецФункции

#Область ПроверкаДоступаПриИзменении

// Для процедур ПроверитьДоступПередЗаписьюИсточника и др.
Процедура ПредварительнаяБлокировкаПередЗаписьюНовогоВФайловойИБ()
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("РегистрСведений.КлючиДоступаКОбъектам");
	Блокировка.Добавить("РегистрСведений.КлючиДоступаГруппДоступа");
	Блокировка.Добавить("РегистрСведений.КлючиДоступаНаборовГруппДоступа");
	Блокировка.Добавить("РегистрСведений.КлючиДоступаПользователей");
	Блокировка.Добавить("РегистрСведений.КлючиДоступаВнешнихПользователей");
	Блокировка.Добавить("Справочник.КлючиДоступа");
	Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
	ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ТаблицыГруппДоступа");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ГруппыЗначенийДоступа");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЗначенияГруппДоступа");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ЗначенияГруппДоступаПоУмолчанию");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.НастройкиПравОбъектов");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.НаследованиеНастроекПравОбъектов");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	Блокировка.Заблокировать();
	
КонецПроцедуры

// Для обработчиков подписок на событие ПередЗаписью.
Процедура ПроверитьДоступПередЗаписьюИсточника(Источник, Отказ, ЭтоНаборЗаписей, Замещение)
	
	Если ПропуститьПроверкуДоступа(Отказ, Источник) Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ЭтоНаборЗаписей
	   И Источник.ЭтоНовый()
	   И ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		
		ПредварительнаяБлокировкаПередЗаписьюНовогоВФайловойИБ();
	КонецЕсли;
	
	ЭтоПолноправныйПользователь = Пользователи.ЭтоПолноправныйПользователь()
		Или УправлениеДоступомСлужебныйПовтИсп.ЭтоПользовательБезОграниченияДоступа()
		Или Не УправлениеДоступом.ПроизводительныйВариант();
	
	Источник.ДополнительныеСвойства.Вставить("УправлениеДоступомИдентификаторТранзакции",
		Новый УникальныйИдентификатор);
	
	ЕстьСтараяВерсия =  ЭтоНаборЗаписей И Замещение
	             Или Не ЭтоНаборЗаписей И Не Источник.ЭтоНовый();
	
	Если ЕстьСтараяВерсия Тогда
		ЗапомнитьДанныеВлияющиеНаЗависимыеКлючиДоступа(Источник, ЭтоНаборЗаписей, Замещение);
		ПроверитьДоступКИсточнику(Источник, Истина, ЭтоНаборЗаписей, Замещение, ЭтоПолноправныйПользователь);
		
	ИначеЕсли Не ЭтоНаборЗаписей Тогда
		УстановитьРазрешенныйКлючДоступаДляНовогоОбъекта(Источник, ЭтоПолноправныйПользователь);
	КонецЕсли;
	
	Если ЭтоНаборЗаписей Тогда
		ЗаписатьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолейПередЗаписью(Источник, ЭтоПолноправныйПользователь);
	КонецЕсли;
	
КонецПроцедуры

// Для обработчиков подписок на событие ПриЗаписи.
Процедура ПроверитьДоступПриЗаписиИсточника(Источник, Отказ, ЭтоНаборЗаписей, Замещение)
	
	Если ПропуститьПроверкуДоступа(Отказ, Источник) Тогда
		Возврат;
	КонецЕсли;
	
	ЭтоПолноправныйПользователь = Пользователи.ЭтоПолноправныйПользователь()
		Или УправлениеДоступомСлужебныйПовтИсп.ЭтоПользовательБезОграниченияДоступа()
		Или Не УправлениеДоступом.ПроизводительныйВариант();
	
	// Проверка доступа к новой версии.
	ПроверитьДоступКИсточнику(Источник, Ложь, ЭтоНаборЗаписей, Замещение, ЭтоПолноправныйПользователь);
	
	ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступа(Источник, ЭтоНаборЗаписей, Ложь);
	
КонецПроцедуры

// Для обработчиков подписок на событие ПередУдалением.
Процедура ПроверитьДоступПередУдалениемИсточника(Источник, Отказ)
	
	Если ПропуститьПроверкуДоступа(Отказ, Источник) Тогда
		Возврат;
	КонецЕсли;
	
	Источник.ДополнительныеСвойства.Вставить("УправлениеДоступомИдентификаторТранзакции",
		Новый УникальныйИдентификатор);
	
	ЗапомнитьДанныеВлияющиеНаЗависимыеКлючиДоступа(Источник, Ложь, Ложь);
	ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступа(Источник, Ложь, Истина);
	
КонецПроцедуры


// Для процедур ПроверитьДоступПередЗаписьюИсточника, ПроверитьДоступПриЗаписиИсточника.
Функция ПропуститьПроверкуДоступа(Отказ, Источник)
	
	Если СтандартныеПодсистемыСервер.ЭтоИдентификаторОбъектаМетаданных(Источник) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если УправлениеДоступомСлужебныйПовтИсп.РазделенныеДанныеНедоступны() Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Не ОграничиватьДоступНаУровнеЗаписейУниверсально(Ложь) Тогда
		Возврат Истина;
	КонецЕсли;
	
	ОтключениеОбновления = ПараметрыСеанса.ОтключениеОбновленияКлючейДоступа;  // См. НовоеОтключениеОбновленияКлючейДоступа
	Если ОтключениеОбновления.Полное Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если ОтключениеОбновления.Стандартное Тогда
		Кэш = УправлениеДоступомСлужебныйПовтИсп.КэшИзмененныхСписковПриОтключенномОбновленииКлючейДоступа();
		ТипИсточника = ТипЗнч(Источник);
		Если Кэш.Получить(ТипИсточника) <> Неопределено Тогда
			Возврат Истина;
		КонецЕсли;
		ОтключениеОбновления = Новый Структура(ОтключениеОбновления);
		ИзмененныеСписки = ОтключениеОбновления.ИзмененныеСписки.Получить();
		ИзмененныеСписки.Вставить(ТипИсточника, Истина);
		ОтключениеОбновления.ИзмененныеСписки = Новый ХранилищеЗначения(ИзмененныеСписки);
		
		УстановитьОтключениеБезопасногоРежима(Истина);
		УстановитьПривилегированныйРежим(Истина);
		ПараметрыСеанса.ОтключениеОбновленияКлючейДоступа = Новый ФиксированнаяСтруктура(ОтключениеОбновления);
		УстановитьПривилегированныйРежим(Истина);
		УстановитьОтключениеБезопасногоРежима(Истина);
		
		Кэш.Вставить(ТипИсточника, Истина);
		Возврат Истина;
	КонецЕсли;
	
	Если Отказ Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Для процедуры ПроверитьДоступПередЗаписьюИсточника.
Процедура ЗапомнитьДанныеВлияющиеНаЗависимыеКлючиДоступа(Источник, ЭтоНаборЗаписей, Замещение)
	
	ПолноеИмя = Источник.Метаданные().ПолноеИмя();
	СвойстваСпискаКакВедущего = СвойстваСпискаКакВедущего(ПолноеИмя,
		Источник.ДополнительныеСвойства.УправлениеДоступомИдентификаторТранзакции);
	
	Если СвойстваСпискаКакВедущего = Неопределено
	 Или СвойстваСпискаКакВедущего.ПоЗначениямПолей = Неопределено Тогда
		
		Источник.ДополнительныеСвойства.Вставить(
			"УправлениеДоступомЗначенияПолейДляПроверкиИзмененияПриЗаписи");
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	
	Если ЭтоНаборЗаписей Тогда
		ОтборПоИзмерениям = ОтборПоИзмерениямНабораЗаписей(Источник, Запрос);
		Запрос.Текст = СтрЗаменить(СвойстваСпискаКакВедущего.ПоЗначениямПолей.ТекстЗапроса,
			"&ОтборПоИзмерениям", ОтборПоИзмерениям);
	Иначе
		Запрос.Текст = СвойстваСпискаКакВедущего.ПоЗначениямПолей.ТекстЗапроса;
		Запрос.УстановитьПараметр("СсылкаНаОбъект", Источник.Ссылка);
	КонецЕсли;
	
	Источник.ДополнительныеСвойства.Вставить(
		"УправлениеДоступомЗначенияПолейДляПроверкиИзмененияПриЗаписи",
		Запрос.ВыполнитьПакет());
	
КонецПроцедуры

// Для процедур ЗапомнитьДанныеВлияющиеНаЗависимыеКлючиДоступа, ПроверитьДоступКНаборуЗаписей.
Функция ОтборПоИзмерениямНабораЗаписей(НаборЗаписей, Запрос, ЗапросНовыхКомбинаций = Неопределено)
	
	ОтборПоИзмерениям = "";
	
	Для Каждого ЭлементОтбора Из НаборЗаписей.Отбор Цикл
		Если Не ЭлементОтбора.Использование Тогда
			Продолжить;
		КонецЕсли;
		ОтборПоИзмерениям = ОтборПоИзмерениям + ?(ОтборПоИзмерениям = "", "", "
		|	И ") + "ТекущаяТаблица." + ЭлементОтбора.Имя + " = &ЗначениеПоля" + ЭлементОтбора.Имя; // @query-part-1
		
		Если Запрос <> Неопределено Тогда
			Запрос.УстановитьПараметр("ЗначениеПоля" + ЭлементОтбора.Имя, ЭлементОтбора.Значение);
		КонецЕсли;
		Если ЗапросНовыхКомбинаций <> Неопределено Тогда
			ЗапросНовыхКомбинаций.УстановитьПараметр("ЗначениеПоля" + ЭлементОтбора.Имя, ЭлементОтбора.Значение);
		КонецЕсли;
	КонецЦикла;
	
	Если ОтборПоИзмерениям = "" Тогда
		ОтборПоИзмерениям = "ИСТИНА";
	КонецЕсли;
	
	Возврат ОтборПоИзмерениям;
	
КонецФункции

// Для процедуры ПроверитьДоступПередЗаписьюИсточника.
Процедура УстановитьРазрешенныйКлючДоступаДляНовогоОбъекта(Источник, ЭтоПолноправныйПользователь)
	
	Если ЭтоПолноправныйПользователь Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	СсылкаНового = ПользователиСлужебный.СсылкаОбъекта(Источник);
	РазрешенныйКлючДоступа = УправлениеДоступомСлужебныйПовтИсп.РазрешенныйКлючДоступа();
	
	НаборЗаписей = РегистрыСведений.КлючиДоступаКОбъектам.СоздатьНаборЗаписей();
	НаборЗаписей.Отбор.Объект.Установить(СсылкаНового);
	
	Запись = НаборЗаписей.Добавить();
	Запись.Объект = СсылкаНового;
	Запись.КлючДоступаПользователей        = РазрешенныйКлючДоступа;
	Запись.КлючДоступаВнешнихПользователей = РазрешенныйКлючДоступа;
	
	НаборЗаписей.Записать();
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Для процедур ПроверитьДоступПередЗаписьюИсточника, ПроверитьДоступПриЗаписиИсточника.
Процедура ПроверитьДоступКИсточнику(Источник, ПередЗаписью, ЭтоНаборЗаписей, Замещение, ЭтоПолноправныйПользователь)
	
	Если ПередЗаписью И ЭтоПолноправныйПользователь Тогда
		Возврат;
	КонецЕсли;
	
	ПолноеИмя = Источник.Метаданные().ПолноеИмя();
	ИдентификаторТранзакции = Источник.ДополнительныеСвойства.УправлениеДоступомИдентификаторТранзакции;
	ВсеПараметрыОграничения = ПараметрыОграниченияПриПроверкеДоступа(Источник, ПолноеИмя, ИдентификаторТранзакции);
	
	Если Пользователи.ЭтоСеансВнешнегоПользователя() Тогда
		ПараметрыОграничения               = ВсеПараметрыОграничения.ДляВнешнихПользователей;
		ПараметрыОграниченияДополнительные = ВсеПараметрыОграничения.ДляПользователей
	Иначе
		ПараметрыОграничения               = ВсеПараметрыОграничения.ДляПользователей;
		ПараметрыОграниченияДополнительные = ВсеПараметрыОграничения.ДляВнешнихПользователей
	КонецЕсли;
	
	Если ПараметрыОграничения.ДоступЗапрещен И Не ЭтоПолноправныйПользователь Тогда
		Если ПараметрыОграничения.ДляВнешнихПользователей Тогда
			ШаблонОшибки =
				НСтр("ru = 'Внешним пользователям запрещен доступ к данным списка
				           |""%1"".'");
		Иначе
			ШаблонОшибки =
				НСтр("ru = 'Пользователям запрещен доступ к данным списка
				           |""%1"".'");
		КонецЕсли;
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
			Источник.Метаданные().Представление());
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ЭтоНаборЗаписей Тогда
		ПроверитьДоступКНаборуЗаписей(Источник, ПередЗаписью, Замещение, ЭтоПолноправныйПользователь,
			ИдентификаторТранзакции, ПараметрыОграничения, ПараметрыОграниченияДополнительные);
	Иначе
		ПроверитьДоступКОбъекту(Источник, ПередЗаписью, ЭтоПолноправныйПользователь,
			ИдентификаторТранзакции, ПараметрыОграничения, ПараметрыОграниченияДополнительные);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ПроверитьДоступКИсточнику.
//
// Возвращаемое значение:
//   см. РассчитанныеПараметрыОграничения
//
Функция ПараметрыОграниченияПриПроверкеДоступа(Источник, ПолноеИмя, ИдентификаторТранзакции)
	
	Возврат ПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции, Null);
	
КонецФункции

// Для процедуры ПроверитьДоступКИсточнику.
Процедура ПроверитьДоступКОбъекту(Источник, ПередЗаписью, ЭтоПолноправныйПользователь,
			ИдентификаторТранзакции, ПараметрыОграничения, ПараметрыОграниченияДополнительные)
	
	ЭтоНовый = Не Источник.ДополнительныеСвойства.Свойство(
		"УправлениеДоступомЗначенияПолейДляПроверкиИзмененияПриЗаписи");
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Если Не ПередЗаписью Тогда
		КлючДоступаОбновлен = Ложь;
		
		ОбновитьКлючДоступаОбъектаПриЗаписи(Источник, ЭтоНовый, ИдентификаторТранзакции,
			ПараметрыОграничения, КлючДоступаОбновлен);
		
		ОбновитьКлючДоступаОбъектаПриЗаписи(Источник, ЭтоНовый, ИдентификаторТранзакции,
			ПараметрыОграниченияДополнительные);
		
		Если Не КлючДоступаОбновлен Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Если ПараметрыОграничения.ОграничениеОтключено Или ЭтоПолноправныйПользователь Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст = ПараметрыОграничения.ТекстЗапросаПроверкиПравЧтениеИзменение;
	
	ПроверкаДобавления = Ложь;
	Если Не ПередЗаписью Тогда
		УточнитьПравоДобавление(Запрос, ЭтоНовый, ПараметрыОграничения, ПроверкаДобавления);
	КонецЕсли;
	
	ОбновитьРазрешенныеНаборыВПараметрахСеанса();
	
	Запрос.УстановитьПараметр("Объект", Источник.Ссылка);
	УстановитьРазрешенныеНаборыВПараметрыЗапроса(Запрос);
	
	Если ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
		Запрос.УстановитьПараметр("ИдентификаторТаблицыНастроекПрав",
			ПараметрыОграничения.ИдентификаторТаблицыНастроекПрав);
	КонецЕсли;
	
	Если Не РезультатЗапросаПроверкиДоступа(Запрос, Источник).Пустой() Тогда
		Возврат; // Доступ разрешен.
	КонецЕсли;
	
	Если ПроверкаДобавления Тогда
		Запрос.Текст = ПараметрыОграничения.ТекстЗапросаПроверкиПравЧтениеИзменение;
		ПравоИзменения = Не Запрос.Выполнить().Пустой();
	Иначе
		ПравоИзменения = Ложь;
	КонецЕсли;
	
	Если ПараметрыОграничения.ОграничениеЧтенияОтключено Тогда
		ПравоЧтения = Истина;
	Иначе
		Запрос.Текст = ПараметрыОграничения.ТекстЗапросаПроверкиПраваЧтение;
		ПравоЧтения = Не Запрос.Выполнить().Пустой();
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
	Данные = ?(ПередЗаписью, Источник.Ссылка, Источник);
	СообщитьОбОшибкеДоступа(Данные, ПередЗаписью, ПравоЧтения, ПравоИзменения, ЭтоНовый);
	
КонецПроцедуры

// Для процедуры ПроверитьДоступКОбъекту и функции ДоступРазрешен.
Процедура УточнитьПравоДобавление(Запрос, ЭтоНовый, ПараметрыОграничения, ПроверкаДобавления)
	
	ПроверкаДобавления = ЭтоНовый И Не ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу;
	Если Не ПроверкаДобавления Тогда
		Возврат;
	КонецЕсли;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"РазрешенныеКлючиДоступа.ПравоИзменение", "РазрешенныеКлючиДоступа.ПравоДобавление");
	
КонецПроцедуры

// Для процедуры ПроверитьДоступКОбъекту.
Процедура ОбновитьКлючДоступаОбъектаПриЗаписи(Источник, ЭтоНовый, ИдентификаторТранзакции,
			ПараметрыОграничения, КлючДоступаОбновлен = Ложь)
	
	Если ПараметрыОграничения.ДоступЗапрещен
	 Или (ПараметрыОграничения.ОграничениеОтключено
	      И Не ПараметрыОграничения.СЗаписьюКлючаДоступаДляЗависимыхСписковБезКлючей) Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ЭтоНовый И Не КлючДоступаИсточникаУстарел(Источник.Ссылка, ПараметрыОграничения, Источник) Тогда
		Возврат;
	КонецЕсли;
	
	ОбновитьКлючиДоступаЭлементовДанныхПриЗаписи(Источник.Ссылка,
		ПараметрыОграничения, ИдентификаторТранзакции,,, Источник);
	
	КлючДоступаОбновлен = Истина;
	
КонецПроцедуры

// Для процедур ПроверитьДоступКОбъекту, ПроверитьДоступКНаборуЗаписей.
Функция РезультатЗапросаПроверкиДоступа(Запрос, Источник)
	
	Возврат Запрос.Выполнить();
	
КонецФункции

// Для процедуры ПроверитьДоступКИсточнику.
Процедура ПроверитьДоступКНаборуЗаписей(Источник, ПередЗаписью, Замещение, ЭтоПолноправныйПользователь,
			ИдентификаторТранзакции, ПараметрыОграничения, ПараметрыОграниченияДополнительные)
	
	Если    ПередЗаписью И (Не Замещение Или ЭтоПолноправныйПользователь)
	 Или Не ПередЗаписью И Источник.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ОтборПоИзмерениям = "";
	Запрос = ?(ПараметрыОграничения.ОграничениеОтключено Или ЭтоПолноправныйПользователь,
		Неопределено, Новый Запрос);
	
	ОбновитьКлючиДоступаКНаборуЗаписей(Источник, ПередЗаписью, Замещение,
		ИдентификаторТранзакции, ПараметрыОграничения, ОтборПоИзмерениям, Запрос);
	
	ОбновитьКлючиДоступаКНаборуЗаписей(Источник, ПередЗаписью, Замещение,
		ИдентификаторТранзакции, ПараметрыОграниченияДополнительные, "", Неопределено);
	
	Если Запрос = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Запрос.Текст = СтрЗаменить(ПараметрыОграничения.ТекстЗапросаПроверкиПравЧтениеИзменение,
		"&ОтборПоИзмерениям", ОтборПоИзмерениям);
	
	ОбновитьРазрешенныеНаборыВПараметрахСеанса();
	УстановитьРазрешенныеНаборыВПараметрыЗапроса(Запрос);
	
	Если РезультатЗапросаПроверкиДоступа(Запрос, Источник).Пустой() Тогда
		Возврат; // Доступ разрешен.
	КонецЕсли;
	
	Если ПараметрыОграничения.ОграничениеЧтенияОтключено Тогда
		ПравоЧтения = Истина;
	Иначе
		Запрос.Текст = СтрЗаменить(ПараметрыОграничения.ТекстЗапросаПроверкиПраваЧтение,
			"&ОтборПоИзмерениям", ОтборПоИзмерениям);
		ПравоЧтения = Запрос.Выполнить().Пустой();
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
	СообщитьОбОшибкеДоступа(Источник, ПередЗаписью, ПравоЧтения, Ложь, Ложь);
	
КонецПроцедуры

// Для процедуры ПроверитьДоступКНаборуЗаписей и для функции ДоступРазрешен.
Процедура ОбновитьКлючиДоступаКНаборуЗаписей(Источник, ПередЗаписью, Замещение,
			ИдентификаторТранзакции, ПараметрыОграничения, ОтборПоИзмерениям, Запрос)
	
	Если ПараметрыОграничения.ДоступЗапрещен
	 Или ПараметрыОграничения.ОграничениеОтключено
	 Или ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу
	   И Запрос = Неопределено Тогда
		
		Возврат;
	КонецЕсли;
	
	Если Не ПередЗаписью Тогда
		ЗапросНовыхКомбинаций = Новый Запрос;
	КонецЕсли;
	
	Если Замещение Тогда
		ОтборПоИзмерениям = ОтборПоИзмерениямНабораЗаписей(Источник, Запрос, ЗапросНовыхКомбинаций);
	Иначе
		КомбинацииЗначенийОпорныхПолей = КомбинацииЗначенийОпорныхПолей(Источник,
			ОтборПоИзмерениям, ПараметрыОграничения);
		
		Если Запрос <> Неопределено Тогда
			Запрос.УстановитьПараметр("КомбинацииЗначенийОпорныхПолей", КомбинацииЗначенийОпорныхПолей);
		КонецЕсли;
		ЗапросНовыхКомбинаций.УстановитьПараметр("КомбинацииЗначенийОпорныхПолей", КомбинацииЗначенийОпорныхПолей);
	КонецЕсли;
	
	Если ПередЗаписью Или ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ОбновитьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолей(ЗапросНовыхКомбинаций,
		ОтборПоИзмерениям, Источник, ПараметрыОграничения);
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Для процедуры ПроверитьДоступПередЗаписьюИсточника.
Процедура ЗаписатьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолейПередЗаписью(Источник, ЭтоПолноправныйПользователь)
	
	Если ЭтоПолноправныйПользователь Или Источник.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ПолноеИмя = Источник.Метаданные().ПолноеИмя();
	ИдентификаторТранзакции = Источник.ДополнительныеСвойства.УправлениеДоступомИдентификаторТранзакции;
	ПараметрыОграничения = ПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции);
	
	Если ПараметрыОграничения.ОграничениеОтключено
	 Или ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
		Возврат;
	КонецЕсли;
	
	ЗапросНовыхКомбинаций = Новый Запрос;
	ОтборПоИзмерениям = "";
	
	ЗапросНовыхКомбинаций.УстановитьПараметр("КомбинацииЗначенийОпорныхПолей",
		КомбинацииЗначенийОпорныхПолей(Источник, ОтборПоИзмерениям, ПараметрыОграничения));
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ОбновитьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолей(ЗапросНовыхКомбинаций,
			ОтборПоИзмерениям, Источник, ПараметрыОграничения);
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Для процедур ПроверитьДоступКНаборуЗаписей,
// ЗаписатьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолейПередЗаписью.
//
Функция КомбинацииЗначенийОпорныхПолей(Источник, ОтборПоИзмерениям, ПараметрыОграничения)
	
	Поля = СтрСоединить(ПараметрыОграничения.ОпорныеПоля.Используемые, ",");
	Поля = УправлениеДоступомСлужебныйПовтИсп.ПоляВРегистреСимволовМетаданных(ПараметрыОграничения.Список, Поля);
	КомбинацииЗначенийОпорныхПолей = Источник.Выгрузить(, Поля);
	КомбинацииЗначенийОпорныхПолей.Свернуть(Поля);
	
	ПоляОтбора = "";
	Для Каждого Поле Из ПараметрыОграничения.ОпорныеПоля.Используемые Цикл
		ПоляОтбора = ПоляОтбора + ?(ПоляОтбора = "", "", ", ") + "ТекущаяТаблица." + Поле;
	КонецЦикла;
	ОтборПоИзмерениям = "(" + ПоляОтбора + ") В (&КомбинацииЗначенийОпорныхПолей)"; // @query-part-2
	
	Возврат КомбинацииЗначенийОпорныхПолей;
	
КонецФункции

// Для процедур ПроверитьДоступКНаборуЗаписей,
// ЗаписатьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолейПередЗаписью.
//
Процедура ОбновитьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолей(ЗапросНовыхКомбинаций,
			ОтборПоИзмерениям, Источник, ПараметрыОграничения)
	
	ЭлементыДанных = ЭлементыДанныхНовыхКомбинацийЗначенийОпорныхПолей(ЗапросНовыхКомбинаций,
		ОтборПоИзмерениям, Источник, ПараметрыОграничения);
	
	Если ЭлементыДанных.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ОбновитьКлючиДоступаЭлементовДанныхПриЗаписи(ЭлементыДанных, ПараметрыОграничения,
		Источник.ДополнительныеСвойства.УправлениеДоступомИдентификаторТранзакции,,, Источник);
	
КонецПроцедуры

// Для процедуры ОбновитьКлючиДоступаНовыхКомбинацийЗначенийОпорныхПолей.
Функция ЭлементыДанныхНовыхКомбинацийЗначенийОпорныхПолей(ЗапросНовыхКомбинаций,
			ОтборПоИзмерениям, Источник, ПараметрыОграничения)
	
	Если ЗапросНовыхКомбинаций.Параметры.Свойство("КомбинацииЗначенийОпорныхПолей") Тогда
		ЗапросНовыхКомбинаций.Текст =
			ПараметрыОграничения.ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейНовыхЗаписей;
	Иначе
		ЗапросНовыхКомбинаций.Текст = СтрЗаменить(
			ПараметрыОграничения.ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейСуществующихЗаписей,
			"&ОтборПоИзмерениям",
			ОтборПоИзмерениям);
	КонецЕсли;
	
	РезультатЗапросаНовыхКомбинаций = ЗапросНовыхКомбинаций.Выполнить();
	Если РезультатЗапросаНовыхКомбинаций.Пустой() Тогда
		Возврат Новый Массив;
	КонецЕсли;
	
	ЭлементыДанных = РезультатЗапросаНовыхКомбинаций.Выгрузить();
	ЭлементыДанных.Колонки.Добавить("ТекущаяСсылка", Новый ОписаниеТипов("Число"));
	Индекс = 0;
	Для Каждого ЭлементДанных Из ЭлементыДанных Цикл
		ЭлементДанных.ТекущаяСсылка = Индекс;
		Индекс = Индекс + 1;
	КонецЦикла;
	
	Возврат ЭлементыДанных;
	
КонецФункции

// Для процедур ПроверитьДоступКОбъекту, ПроверитьДоступКНаборуЗаписей.
Процедура СообщитьОбОшибкеДоступа(Данные, СтараяВерсия, ЕстьПравоЧтения, ЕстьПравоИзменения, ЭтоНовый)
	
	Если СтараяВерсия Тогда
		Если ЕстьПравоЧтения Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Недостаточно прав для изменения данных:
				           |%1'"), ПредставлениеДанных(Данные));
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Недостаточно прав для чтения данных:
				           |%1'"), ПредставлениеДанных(Данные));
		КонецЕсли;
	Иначе
		Если ЕстьПравоЧтения И ЕстьПравоИзменения Тогда
			Если ЭтоНовый Тогда
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Недостаточно прав для добавления данных (нет права добавления):
					           |%1'"), ПредставлениеДанных(Данные));
			Иначе
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Недостаточно прав для добавления данных (нет права добавления для сделанных изменений):
					           |%1'"), ПредставлениеДанных(Данные));
			КонецЕсли;
		ИначеЕсли ЕстьПравоЧтения Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Недостаточно прав для добавления данных (невозможно будет изменить):
				           |%1'"), ПредставлениеДанных(Данные));
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Недостаточно прав для добавления данных (невозможно будет прочитать):
				           |%1.'"), ПредставлениеДанных(Данные));
		КонецЕсли;
	КонецЕсли;
	
	ВызватьИсключение ТекстОшибки;
	
КонецПроцедуры

// Для процедуры СообщитьОбОшибкеДоступа.
Функция ПредставлениеДанных(Данные)
	
	Если ТипЗнч(Данные) = Тип("Строка") Тогда
		Возврат СокрЛП(Данные);
	КонецЕсли;
	
	Если ТипЗнч(Данные) = Тип("Структура") Тогда
		ЭтоРегистр = Истина;
		Если ТипЗнч(Данные.Регистр) = Тип("Строка") Тогда
			ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Данные.Регистр);
		Иначе
			ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(Данные.Регистр));
		КонецЕсли;
	Иначе
		ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(Данные));
		ЭтоРегистр = ОбщегоНазначения.ЭтоРегистр(ОбъектМетаданных);
	КонецЕсли;
	
	Если ОбъектМетаданных = Неопределено Тогда
		Возврат "";
	КонецЕсли;
	
	Если ЭтоРегистр Тогда
		ПредставлениеДанных = ОбъектМетаданных.Представление();
		
		КоличествоПолей = 0;
		Для каждого ЭлементОтбора Из Данные.Отбор Цикл
			Если ЭлементОтбора.Использование Тогда
				КоличествоПолей = КоличествоПолей + 1;
			КонецЕсли;
		КонецЦикла;
		
		Если КоличествоПолей = 1 Тогда
			ПредставлениеДанных = ПредставлениеДанных
				+ " " + НСтр("ru = 'с полем'")  + " " + Строка(Данные.Отбор);
			
		ИначеЕсли КоличествоПолей > 1 Тогда
			ПредставлениеДанных = ПредставлениеДанных
				+ " " + НСтр("ru = 'с полями'") + " " + Строка(Данные.Отбор);
		КонецЕсли;
	Иначе
		ПредставлениеДанных = Строка(Данные);
		ПредставлениеМетаданных = ОбщегоНазначения.ПредставлениеОбъекта(ОбъектМетаданных);
		
		Если Не СтрЗаканчиваетсяНа(ПредставлениеДанных, "(" + ПредставлениеМетаданных + ")")
		   И Не СтрНачинаетсяС(ПредставлениеДанных, ПредставлениеМетаданных) Тогда
			
			ПредставлениеДанных = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1 (%2)",
				ПредставлениеДанных, ПредставлениеМетаданных);
		КонецЕсли;
	КонецЕсли;
		
	Возврат ПредставлениеДанных;
	
КонецФункции

// Для процедуры ПроверитьДоступКОбъекту и формы ОбновлениеДоступаРучноеУправление.
//
// Возвращаемое значение:
//  Булево
//
Функция КлючДоступаИсточникаУстарел(СсылкаНаОбъект, ПараметрыОграничения, Источник = Неопределено) Экспорт
	
	Если ПараметрыОграничения.БезЗаписиКлючейДоступа Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Ссылка", СсылкаНаОбъект);
	
	Если Не ЗначениеЗаполнено(ПараметрыОграничения.СоставПолей) Тогда
		Запрос.УстановитьПараметр("Список",
			ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ПараметрыОграничения.Список));
	КонецЕсли;
	
	Запрос.Текст = ПараметрыОграничения.ТекстЗапросаЭлементовДанныхСУстаревшимиКлючами;
	
	Если ПараметрыОграничения.СписокСДатой Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ТекущийСписок.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания", "ТекущийСписок.Ссылка = &Ссылка"); // @query-part-1 @query-part-2
	Иначе
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ТекущийСписок.Ссылка >= &ПоследняяОбработаннаяСсылка", "ТекущийСписок.Ссылка = &Ссылка"); // @query-part-1 @query-part-2
	КонецЕсли;
	УстановитьУточнениеПланаЗапроса(Запрос.Текст);
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Если Запрос.Выполнить().Пустой() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
	Возврат Истина;
	
КонецФункции

// Для процедур ПроверитьДоступКОбъекту, ПроверитьДоступКНаборуЗаписей и формы ОбновлениеДоступаРучноеУправление.
Процедура ОбновитьКлючиДоступаЭлементовДанныхПриЗаписи(ОписаниеЭлементовДанных, ПараметрыОграничения,
			ИдентификаторТранзакции, ОбновитьПраваНаКлючи = Ложь, ЕстьИзмененияПрав = Ложь, Источник = Неопределено) Экспорт
	
	Если ПараметрыОграничения.БезЗаписиКлючейДоступа Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(ОписаниеЭлементовДанных) = Тип("ТаблицаЗначений") Тогда
		ЭлементыДанных = ОписаниеЭлементовДанных;
	Иначе
		ЭлементыДанных = Новый ТаблицаЗначений;
		ЭлементыДанных.Колонки.Добавить("ТекущаяСсылка");
		ЭлементыДанных.Добавить().ТекущаяСсылка = ОписаниеЭлементовДанных;
	КонецЕсли;
	
	СвойстваСпискаКакВедущего = СвойстваСпискаКакВедущего(ПараметрыОграничения.Список,
		ИдентификаторТранзакции);
	ИмяСвойстваВидаПользователей = ?(ПараметрыОграничения.ДляВнешнихПользователей,
		"ДляВнешнихПользователей", "ДляПользователей");
	
	ПараметрыОбновления = Новый Структура(ПараметрыОграничения);
	ПараметрыОбновления.Вставить("ЕстьИзмененияПрав",       ЕстьИзмененияПрав);
	ПараметрыОбновления.Вставить("ОбновитьПраваНаКлючи",    ОбновитьПраваНаКлючи);
	ПараметрыОбновления.Вставить("ИдентификаторТранзакции", ИдентификаторТранзакции);
	ПараметрыОбновления.Вставить("ИдентификаторСписка",
		ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ПараметрыОграничения.Список));
	
	Если СвойстваСпискаКакВедущего = Неопределено
	 Или СвойстваСпискаКакВедущего.ПоКлючамДоступа = Неопределено
	 Или СвойстваСпискаКакВедущего.ПоКлючамДоступа[ИмяСвойстваВидаПользователей] = Неопределено Тогда
		
		ПараметрыОбновления.Вставить("ЗависимыеСпискиПоКлючамДоступа", Новый Массив);
	Иначе
		ПараметрыОбновления.Вставить("ЗависимыеСпискиПоКлючамДоступа",
			СвойстваСпискаКакВедущего.ПоКлючамДоступа[ИмяСвойстваВидаПользователей]);
	КонецЕсли;
	
	ОбновитьКлючиДоступаПорцииЭлементовДанныхСписка(ЭлементыДанных, ПараметрыОбновления);
	
	ЕстьИзмененияПрав = ПараметрыОбновления.ЕстьИзмененияПрав;
	
КонецПроцедуры

// Для регистра сведений ГруппыЗначенийДоступа.
Процедура ЗапланироватьОбновлениеЗависимыхСписковПоЗначениямСГруппами(ЗначенияСИзменениямиПоТипам) Экспорт
	
	Если Не ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
		Возврат;
	КонецЕсли;
	
	ИдентификаторТранзакции = Новый УникальныйИдентификатор;
	
	СпискиДляОбновления = Новый Соответствие;
	Для Каждого ОписаниеЗначений Из ЗначенияСИзменениямиПоТипам Цикл
		ПолноеИмяВедущего = Метаданные.НайтиПоТипу(ОписаниеЗначений.Ключ).ПолноеИмя();
		СвойстваСпискаКакВедущего = СвойстваСпискаКакВедущего(ПолноеИмяВедущего, ИдентификаторТранзакции);
		Если СвойстваСпискаКакВедущего = Неопределено
		 Или СвойстваСпискаКакВедущего.ПоЗначениямСГруппами = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ДобавитьСпискиДляОбновленияДляВидаПользователей(СпискиДляОбновления,
			СвойстваСпискаКакВедущего.ПоЗначениямСГруппами, "ДляПользователей");
		
		ДобавитьСпискиДляОбновленияДляВидаПользователей(СпискиДляОбновления,
			СвойстваСпискаКакВедущего.ПоЗначениямСГруппами, "ДляВнешнихПользователей");
	КонецЦикла;
	
	ЗапланироватьОбновлениеУстаревшихКлючейДоступа(СпискиДляОбновления,
		ИдентификаторТранзакции,
		"ЗапланироватьОбновлениеЗависимыхСписковПоЗначениямСГруппами",
		?(ЗначенияСИзменениямиПоТипам.Количество() <> 1 Или ОписаниеЗначений.Значение = Истина,
			Неопределено, Новый Структура("ПоЗначениямСГруппами", ОписаниеЗначений.Значение)));
	
КонецПроцедуры

// Для процедуры ЗапланироватьОбновлениеЗависимыхСписковПоЗначениямСГруппами.
Процедура ДобавитьСпискиДляОбновленияДляВидаПользователей(СпискиДляОбновления, ПоЗначениямСГруппами, ИмяВидаПользователей)
	
	ПолныеИмена = ПоЗначениямСГруппами[ИмяВидаПользователей];
	Если ПолныеИмена = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Для Каждого ПолноеИмя Из ПолныеИмена Цикл
		СписокДляОбновления = СпискиДляОбновления.Получить(ПолноеИмя);
		Если СписокДляОбновления = Неопределено Тогда
			СписокДляОбновления = Новый Структура("ДляПользователей, ДляВнешнихПользователей", Ложь, Ложь);
			СпискиДляОбновления.Вставить(ПолноеИмя, СписокДляОбновления);
		КонецЕсли;
		СписокДляОбновления[ИмяВидаПользователей] = Истина;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступа, ЗаписатьКлючиДоступаОбъектов,
// ЗапланироватьОбновлениеЗависимыхСписковПоЗначениямСГруппами.
//
Процедура ЗапланироватьОбновлениеУстаревшихКлючейДоступа(СпискиДляОбновления, ИдентификаторТранзакции,
			Описание, ВедущийОбъект = Неопределено, ЭтоПродолжениеОбновления = Ложь)
	
	Если ТипЗнч(СпискиДляОбновления) = Тип("Соответствие")
	   И СпискиДляОбновления.Количество() = 0
	 Или ТипЗнч(СпискиДляОбновления) = Тип("Структура")
	   И СпискиДляОбновления.ИменаСписков.Количество() = 0 Тогда
		
		Возврат;
	КонецЕсли;
	
	Списки = Новый Массив;
	СпискиДляПользователей = Новый Массив;
	СпискиДляВнешнихПользователей = Новый Массив;
	
	Если ТипЗнч(СпискиДляОбновления) = Тип("Соответствие") Тогда
		Для Каждого ОписаниеСписка Из СпискиДляОбновления Цикл
			ПолноеИмя = ОписаниеСписка.Ключ;
			Свойства  = ОписаниеСписка.Значение;
			
			Если Свойства.ДляПользователей Тогда
				ПараметрыОграничения = ПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции, Ложь);
				
				Если ПараметрыОграничения.ОграничениеОтключено
				 Или ПараметрыОграничения.ДоступЗапрещен
				 Или ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
					
					Свойства.ДляПользователей = Ложь;
				КонецЕсли;
			КонецЕсли;
			
			Если Свойства.ДляВнешнихПользователей Тогда
				ПараметрыОграничения = ПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции, Истина);
				
				Если ПараметрыОграничения.ОграничениеОтключено
				 Или ПараметрыОграничения.ДоступЗапрещен
				 Или ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
					
					Свойства.ДляВнешнихПользователей = Ложь;
				КонецЕсли;
			КонецЕсли;
			
			Если Свойства.ДляПользователей И Свойства.ДляВнешнихПользователей Тогда
				Списки.Добавить(ПолноеИмя);
			
			ИначеЕсли Свойства.ДляПользователей Тогда
				СпискиДляПользователей.Добавить(ПолноеИмя);
				
			ИначеЕсли Свойства.ДляВнешнихПользователей Тогда
				СпискиДляВнешнихПользователей.Добавить(ПолноеИмя);
			КонецЕсли;
		КонецЦикла;
	Иначе
		Для Каждого ПолноеИмя Из СпискиДляОбновления.ИменаСписков Цикл
			ПараметрыОграничения = ПараметрыОграничения(ПолноеИмя,
				ИдентификаторТранзакции, СпискиДляОбновления.ДляВнешнихПользователей);
			
			Если ПараметрыОграничения.ОграничениеОтключено
			 Или ПараметрыОграничения.ДоступЗапрещен
			 Или ПараметрыОграничения.ИспользуетсяОграничениеПоВладельцу Тогда
				
				Продолжить;
			КонецЕсли;
			
			Если СпискиДляОбновления.ДляВнешнихПользователей Тогда
				СпискиДляВнешнихПользователей.Добавить(ПолноеИмя);
			Иначе
				СпискиДляПользователей.Добавить(ПолноеИмя);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если Описание = "ЗаполнитьКэшПараметровОграниченияДоступа" Тогда
		Возврат;
	КонецЕсли;
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
	ПараметрыПланирования.РазрешенныеКлючиДоступа = Ложь;
	ПараметрыПланирования.Описание = Описание;
	ПараметрыПланирования.ВедущийОбъект = ВедущийОбъект;
	ПараметрыПланирования.ЭтоПродолжениеОбновления = ЭтоПродолжениеОбновления;
	ЗапланироватьОбновлениеДоступа(Списки, ПараметрыПланирования);
	
	ПараметрыПланирования.ДляВнешнихПользователей = Ложь;
	ЗапланироватьОбновлениеДоступа(СпискиДляПользователей, ПараметрыПланирования);
	
	ПараметрыПланирования.ДляПользователей = Ложь;
	ПараметрыПланирования.ДляВнешнихПользователей = Истина;
	ЗапланироватьОбновлениеДоступа(СпискиДляВнешнихПользователей, ПараметрыПланирования);
	
КонецПроцедуры

// Для процедуры ПроверитьДоступПриЗаписиИсточника, ПроверитьДоступПередУдалениемИсточника.
Процедура ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступа(Источник, ЭтоНаборЗаписей, Удаление)
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ПолноеИмя = Источник.Метаданные().ПолноеИмя();
	СвойстваСпискаКакВедущего = СвойстваСпискаКакВедущего(ПолноеИмя,
		Источник.ДополнительныеСвойства.УправлениеДоступомИдентификаторТранзакции);
	
	Если СвойстваСпискаКакВедущего = Неопределено
	 Или СвойстваСпискаКакВедущего.ПоЗначениямПолей = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если Источник.ДополнительныеСвойства.Свойство("УправлениеДоступомЗначенияПолейДляПроверкиИзмененияПриЗаписи") Тогда
		РезультатыЗапроса = Источник.ДополнительныеСвойства.УправлениеДоступомЗначенияПолейДляПроверкиИзмененияПриЗаписи;
	Иначе
		РезультатыЗапроса = Неопределено;
	КонецЕсли;
	
	ИзмененияПоЗначениямПолей = Новый Структура;
	ИзмененияПоЗначениямПолей.Вставить("Описание", ?(ЭтоНаборЗаписей, Источник.Отбор, Источник.Ссылка));
	ИзмененияПоЗначениямПолей.Вставить("ИзмененнаяТаблица", ПолноеИмя);
	ИзмененияПоЗначениямПолей.Вставить("СоставИзменений");
	
	ВедущийОбъект = ОписаниеВедущегоОбъекта();
	ВедущийОбъект.Вставить("ПоЗначениямПолей", ИзмененияПоЗначениямПолей);
	
	ПоЗначениямПолей = СвойстваСпискаКакВедущего.ПоЗначениямПолей;
	ПоляШапки = ПоЗначениямПолей.ПоляШапки;
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
	ПараметрыПланирования.РазрешенныеКлючиДоступа = Ложь;
	ПараметрыПланирования.Описание = "ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступа";
	ПараметрыПланирования.ВедущийОбъект = ВедущийОбъект;
	
	Если ЭтоНаборЗаписей Тогда
		Если ЗначениеЗаполнено(ПоляШапки.ВсеПоля) Тогда
			РезультатЗапроса = ?(РезультатыЗапроса = Неопределено, Неопределено, РезультатыЗапроса[0]);
			ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступаПоЗначениямПолей(РезультатЗапроса,
				Источник, ПоляШапки.НаборыПолей, ПараметрыПланирования);
		КонецЕсли;
	Иначе
		Если ЗначениеЗаполнено(ПоляШапки.ВсеПоля) Тогда
			РезультатЗапроса = ?(РезультатыЗапроса = Неопределено, Неопределено, РезультатыЗапроса[0]);
			НовыеЗначения = Новый ТаблицаЗначений;
			Для Каждого ИмяПоля Из ПоляШапки.ВсеПоля Цикл
				НовыеЗначения.Колонки.Добавить(ИмяПоля, ПоляШапки.ТипыВсехПолей.Получить(ИмяПоля).Получить());
			КонецЦикла;
			Если Не Удаление Тогда
				ЗаполнитьЗначенияСвойств(НовыеЗначения.Добавить(), Источник);
			КонецЕсли;
			ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступаПоЗначениямПолей(РезультатЗапроса,
				НовыеЗначения, ПоляШапки.НаборыПолей, ПараметрыПланирования);
			Индекс = 1;
		Иначе
			Индекс = 0;
		КонецЕсли;
		Для Каждого ОписаниеТабличнойЧасти Из ПоЗначениямПолей.ТабличныеЧасти Цикл
			РезультатЗапроса = ?(РезультатыЗапроса = Неопределено, Неопределено, РезультатыЗапроса[Индекс]);
			ИзмененияПоЗначениямПолей.ИзмененнаяТаблица = ПолноеИмя + "." + ОписаниеТабличнойЧасти.Имя;
			НовыеЗначения = НовыеЗначенияТабличнойЧасти(Источник, ОписаниеТабличнойЧасти, Удаление);
			// @skip-check query-in-loop - В вызываемом варианте ветка с запросом не используется
			ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступаПоЗначениямПолей(РезультатЗапроса,
				НовыеЗначения, ОписаниеТабличнойЧасти.НаборыПолей, ПараметрыПланирования);
			Индекс = Индекс + 1;
		КонецЦикла;
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//     * ПоЗначениямПолей - Структура:
//         ** Описание          - ЛюбаяСсылка
//                              - Отбор
//         ** ИзмененнаяТаблица - Строка - полное имя списка
//         ** СоставИзменений   - см. СоставИзмененийТаблицы
//     * ПоКлючамДоступа      - СправочникСсылка.КлючиДоступа
//     * ПоЗначениямСГруппами - ОпределяемыйТип.ЗначениеДоступа - только значения доступа с группами.
//     * ПоДаннымКэшаРасчетаПрав - Строка - наименование данных для кэша расчета прав.
//
Функция ОписаниеВедущегоОбъекта()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедуры ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступа.
Функция НовыеЗначенияТабличнойЧасти(Источник, ОписаниеТабличнойЧасти, Удаление)
	
	Поля = Новый Массив(ОписаниеТабличнойЧасти.ВсеПоля);
	
	ИмяПоляСсылка = "Ссылка"; // @Non-NLS
	Индекс = Поля.Найти(ИмяПоляСсылка);
	Если Индекс <> Неопределено Тогда
		Поля.Удалить(Индекс);
	Иначе
		ИмяПоляСсылка = "Ref";
		Индекс = Поля.Найти(ИмяПоляСсылка);
		Если Индекс <> Неопределено Тогда
			Поля.Удалить(Индекс);
		КонецЕсли;
	КонецЕсли;
	
	НовыеЗначения = Источник[ОписаниеТабличнойЧасти.Имя].Выгрузить(
		?(Удаление, Новый Массив, Неопределено), СтрСоединить(Поля, ", ")); // ТаблицаЗначений
	
	Если Индекс <> Неопределено Тогда
		Типы = Новый Массив;
		Типы.Добавить(ТипЗнч(Источник.Ссылка));
		НовыеЗначения.Колонки.Добавить(ИмяПоляСсылка, Новый ОписаниеТипов(Типы));
		НовыеЗначения.ЗаполнитьЗначения(Источник.Ссылка, ИмяПоляСсылка);
	КонецЕсли;
	
	Возврат НовыеЗначения;
	
КонецФункции

// Для процедуры ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступа.
Процедура ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступаПоЗначениямПолей(РезультатЗапроса,
			Источник, НаборыПолейПоВидамПользователей, ПараметрыПланирования)
	
	ВидыПользователей = Новый Массив;
	ВидыПользователей.Добавить("ДляПользователей");
	ВидыПользователей.Добавить("ДляВнешнихПользователей");
	
	Для Каждого ВидПользователей Из ВидыПользователей Цикл
		НаборыПолей = НаборыПолейПоВидамПользователей[ВидПользователей];
		Если НаборыПолей = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Если ВидПользователей = "ДляПользователей" Тогда
			ПараметрыПланирования.ДляВнешнихПользователей = Ложь;
			ПараметрыПланирования.ДляПользователей = Истина;
		Иначе
			ПараметрыПланирования.ДляВнешнихПользователей = Истина;
			ПараметрыПланирования.ДляПользователей = Ложь;
		КонецЕсли;
		Для Каждого ОписаниеНабораПолей Из НаборыПолей Цикл
			СоставИзменений = СоставИзмененийТаблицы(РезультатЗапроса, Источник, ОписаниеНабораПолей.Ключ);
			Если СоставИзменений = Null Тогда
				Продолжить;
			КонецЕсли;
			Для Каждого ОписаниеЗависимыхТаблиц Из ОписаниеНабораПолей.Значение Цикл
				Если ОписаниеЗависимыхТаблиц.Ключ = ОписаниеНабораПолей.Ключ
				 Или СоставИзменений = Неопределено Тогда
					ТекущийСоставИзменений = СоставИзменений;
				Иначе
					ТекущийСоставИзменений = СоставИзменений.Скопировать(, ОписаниеЗависимыхТаблиц.Ключ);
					ТекущийСоставИзменений.Свернуть(ОписаниеЗависимыхТаблиц.Ключ);
				КонецЕсли;
				ПараметрыПланирования.ВедущийОбъект.ПоЗначениямПолей.СоставИзменений = ТекущийСоставИзменений;
				// @skip-check query-in-loop - В вызываемом варианте ветка с запросом не используется
				ЗапланироватьОбновлениеДоступа(ОписаниеЗависимыхТаблиц.Значение, ПараметрыПланирования);
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ЗапланироватьОбновлениеЗависимыхУстаревшихКлючейДоступаПоЗначениямПолей.
//
// Возвращаемое значение:
//   ТаблицаЗначений
//
Функция СоставИзмененийТаблицы(РезультатЗапроса, Источник, Поля)
	
	МаксимумКомбинаций = МаксимальноеКоличествоКомбинацийЗначенийВедущихПолейПриВычисленииСоставаИзмененных();
	Если РезультатЗапроса = Неопределено Тогда
		СтарыеКомбинации = Новый ТаблицаЗначений;
	Иначе
		СтарыеКомбинации = РезультатЗапроса.Выгрузить();
		Если СтрРазделить(Поля, ",").Количество() < СтарыеКомбинации.Колонки.Количество() Тогда
			СтарыеКомбинации.Свернуть(Поля);
		КонецЕсли;
	КонецЕсли;
	
	Если СтарыеКомбинации.Количество() >= МаксимумКомбинаций Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если ТипЗнч(Источник) = Тип("ТаблицаЗначений") Тогда
		НовыеКомбинации = Источник.Скопировать(, Поля);
	Иначе
		НовыеКомбинации = Источник.Выгрузить(, Поля);
	КонецЕсли;
	НовыеКомбинации.Свернуть(Поля);
	
	Если НовыеКомбинации.Количество() >= МаксимумКомбинаций Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	СоставИзменений = НовыеКомбинации.Скопировать(Новый Массив);
	
	НовыеКомбинации.Колонки.Добавить("ВидИзменения", Новый ОписаниеТипов("Число"));
	НовыеКомбинации.ЗаполнитьЗначения(1, "ВидИзменения");
	Для Каждого Строка Из СтарыеКомбинации Цикл
		НоваяСтрока = НовыеКомбинации.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
		НоваяСтрока.ВидИзменения = -1;
	КонецЦикла;
	НовыеКомбинации.Свернуть(Поля, "ВидИзменения");
	
	Для Каждого Строка Из НовыеКомбинации Цикл
		Если Строка.ВидИзменения <> 0 Тогда
			ЗаполнитьЗначенияСвойств(СоставИзменений.Добавить(), Строка);
		КонецЕсли;
	КонецЦикла;
	
	Возврат ?(СоставИзменений.Количество() > 0, СоставИзменений, Null);
	
КонецФункции

Процедура ЗаблокироватьРегистрыПланированияОбновленияКлючейДоступаВФайловойИБ()
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыОграниченияДоступа");
	ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	Блокировка.Заблокировать();
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("РегистрСведений.ОбновлениеКлючейДоступаКДанным");
	Блокировка.Добавить("РегистрСведений.ОбновлениеКлючейДоступаПользователей");
	Блокировка.Заблокировать();
	
КонецПроцедуры

// Для вызова и модуля менеджера справочника ГруппыДоступа.
Процедура ЗапланироватьОбновлениеДоступаПриИзмененииУчастниковГруппыДоступа(ГруппаДоступа,
			ТипыИзмененныхУчастников, ПриЗагрузке = Ложь) Экспорт
	
	Если Не ТипыИзмененныхУчастников.Пользователи
	   И Не ТипыИзмененныхУчастников.ВнешниеПользователи Тогда
		Возврат;
	КонецЕсли;
	
	Описание = ?(ПриЗагрузке,
		"ЗапланироватьОбновлениеДоступаПриИзмененииУчастниковГруппыДоступаПриЗагрузке",
		"ЗапланироватьОбновлениеДоступаПриИзмененииУчастниковГруппыДоступа");
	
	ЗапланироватьОбновлениеКэшаРасчетаПрав(Описание, "УчастникиГруппДоступа");
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ГруппаДоступа", ГруппаДоступа);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ТаблицыГруппДоступа.Таблица КАК Таблица
	|ИЗ
	|	РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа
	|ГДЕ
	|	ТаблицыГруппДоступа.ГруппаДоступа = &ГруппаДоступа";
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Таблицы = Запрос.Выполнить().Выгрузить();
	
	ЗапланироватьОбновлениеПользователейКлючейДоступа(Таблицы, Описание,
		ТипыИзмененныхУчастников.Пользователи, ТипыИзмененныхУчастников.ВнешниеПользователи, Истина);
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Для процедуры ПослеОбновленияСоставовГруппПользователей и
// модуля менеджера справочника ГруппыДоступа.
//
Процедура ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа(ИзмененныеУчастники,
			ПриЗагрузке = Ложь, ПослеУстановкиПользователяИБ = Ложь) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Описание = ?(ПослеУстановкиПользователяИБ,
		"ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступаПослеУстановкиПользователяИБ",
		?(ПриЗагрузке,
			"ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступаПриЗагрузке",
			"ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа"));
	
	ЗапланироватьОбновлениеКэшаРасчетаПрав(Описание, "СоставыГруппПользователей");
	
	Если ИзмененныеУчастники = Неопределено Тогда
		ДляПользователей        = Истина;
		ДляВнешнихПользователей = Истина;
		
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ТаблицыГруппДоступа.Таблица КАК Таблица
		|ИЗ
		|	РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа";
	Иначе
		ДляПользователей        = Ложь;
		ДляВнешнихПользователей = Ложь;
		
		Для Каждого Участник Из ИзмененныеУчастники Цикл
			Если ТипЗнч(Участник) = Тип("СправочникСсылка.Пользователи")
			 Или ТипЗнч(Участник) = Тип("СправочникСсылка.ГруппыПользователей") Тогда
				
				ДляПользователей = Истина;
			
			ИначеЕсли ТипЗнч(Участник) = Тип("СправочникСсылка.ВнешниеПользователи")
			      Или ТипЗнч(Участник) = Тип("СправочникСсылка.ГруппыВнешнихПользователей") Тогда
				
				ДляВнешнихПользователей = Истина;
			КонецЕсли;
		КонецЦикла;
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ИзмененныеУчастники", ИзмененныеУчастники);
		Если ПослеУстановкиПользователяИБ Тогда
			Запрос.Текст =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ТаблицыГруппДоступа.Таблица КАК Таблица
			|ИЗ
			|	РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК УчастникиГруппДоступа
			|		ПО ТаблицыГруппДоступа.ГруппаДоступа = УчастникиГруппДоступа.Ссылка
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
			|		ПО (СоставыГруппПользователей.Пользователь В (&ИзмененныеУчастники))
			|			И (СоставыГруппПользователей.ГруппаПользователей = УчастникиГруппДоступа.Пользователь)";
		Иначе
			Запрос.Текст =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ТаблицыГруппДоступа.Таблица КАК Таблица
			|ИЗ
			|	РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК УчастникиГруппДоступа
			|		ПО ТаблицыГруппДоступа.ГруппаДоступа = УчастникиГруппДоступа.Ссылка
			|			И (УчастникиГруппДоступа.Пользователь В (&ИзмененныеУчастники))";
		КонецЕсли;
	КонецЕсли;
	
	Таблицы = Запрос.Выполнить().Выгрузить();
	
	ЗапланироватьОбновлениеПользователейКлючейДоступа(Таблицы,
		Описание, ДляПользователей, ДляВнешнихПользователей, Истина);
	
	ЗапланироватьОбновлениеНаборовГруппДоступа(Описание, ДляПользователей, ДляВнешнихПользователей);
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Для вызова из модуля менеджера регистра ТаблицыГруппДоступа.
Процедура ЗапланироватьОбновлениеДоступаПриИзмененииТаблицГруппДоступа(Таблицы) Экспорт
	
	Описание = "ЗапланироватьОбновлениеДоступаПриИзмененииТаблицГруппДоступа";
	
	ЗапланироватьОбновлениеКэшаРасчетаПрав(Описание, "ТаблицыГруппДоступа");
	
	ЗапланироватьОбновлениеПользователейКлючейДоступа(Таблицы, Описание, Истина, Истина);
	
КонецПроцедуры

// Для вызова из модуля объекта и модуля менеджера справочника ПрофилиГруппДоступа.
Процедура ЗапланироватьОбновлениеДоступаПриИзмененииРолейПрофиля(Описание, ИзмененныеРоли) Экспорт
	
	ЗапланироватьОбновлениеКэшаРасчетаПрав(Описание, "РолиПрофилейГруппДоступа");
	
	Если ИзмененныеРоли = Неопределено
	 Или ИзмененныеРоли.Количество() > 0 Тогда
		
		ЗапланироватьОбновлениеДоступаПриИзмененииРолей(Описание, ИзмененныеРоли);
	КонецЕсли;
	
КонецПроцедуры

// Для вызова из модуля объекта и модуля менеджера справочника ГруппыДоступа.
Процедура ЗапланироватьОбновлениеДоступаПриИзмененииПрофиляГруппыДоступа(Описание, ИзмененныеРоли, ПрофильИзменен) Экспорт
	
	Если ПрофильИзменен Тогда
		ЗапланироватьОбновлениеКэшаРасчетаПрав(Описание, "ГруппыДоступаПрофилей");
	КонецЕсли;
	
	Если ИзмененныеРоли = Неопределено
	 Или ИзмененныеРоли.Количество() > 0 Тогда
		
		ЗапланироватьОбновлениеДоступаПриИзмененииРолей(Описание, ИзмененныеРоли);
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ЗапланироватьОбновлениеДоступаПриИзмененииРолейПрофиля и
// ЗапланироватьОбновлениеДоступаПриИзмененииПрофиляГруппыДоступа.
//
Процедура ЗапланироватьОбновлениеДоступаПриИзмененииРолей(Описание, ИзмененныеРоли)
	
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь);
	Если Не ЗначениеЗаполнено(ДействующиеПараметры.ВедущиеРоли) Тогда
		Возврат;
	КонецЕсли;
	
	Если ИзмененныеРоли <> Неопределено Тогда
		МетаданныеРолей = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(ИзмененныеРоли, Ложь);
		ИменаРолей = Новый Соответствие;
		Для Каждого КлючИЗначение Из МетаданныеРолей Цикл
			Если ТипЗнч(КлючИЗначение.Значение) = Тип("ОбъектМетаданных") Тогда
				ИменаРолей.Вставить(КлючИЗначение.Значение.Имя, Истина);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	СпискиДляПользователей = Новый Массив;
	СпискиДляВнешнихПользователей = Новый Массив;
	ДобавленныеСпискиДляПользователей = Новый Соответствие;
	ДобавленныеСпискиДляВнешнихПользователей = Новый Соответствие;
	
	Для Каждого ОписаниеВедущейРоли Из ДействующиеПараметры.ВедущиеРоли Цикл
		Если ИзмененныеРоли <> Неопределено
		   И ИменаРолей.Получить(ОписаниеВедущейРоли.Ключ) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ДобавитьСпискиВедущейРолиДляОбновленияПрав(СпискиДляПользователей,
			ОписаниеВедущейРоли.Значение.ДляПользователей, ДобавленныеСпискиДляПользователей);
		ДобавитьСпискиВедущейРолиДляОбновленияПрав(СпискиДляВнешнихПользователей,
			ОписаниеВедущейРоли.Значение.ДляВнешнихПользователей, ДобавленныеСпискиДляВнешнихПользователей);
	КонецЦикла;
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
	ПараметрыПланирования.КлючиДоступаКДанным = Ложь;
	ПараметрыПланирования.Описание = Описание;
	
	ПараметрыПланирования.ДляВнешнихПользователей = Ложь;
	ЗапланироватьОбновлениеДоступа(СпискиДляПользователей, ПараметрыПланирования);
	
	ПараметрыПланирования.ДляПользователей = Ложь;
	ПараметрыПланирования.ДляВнешнихПользователей = Истина;
	ЗапланироватьОбновлениеДоступа(СпискиДляВнешнихПользователей, ПараметрыПланирования);
	
КонецПроцедуры

// Для процедуры ЗапланироватьОбновлениеДоступаПриИзмененииРолей.
Процедура ДобавитьСпискиВедущейРолиДляОбновленияПрав(Списки, СпискиВедущейРоли, ДобавленныеСписки)
	
	Если Не ЗначениеЗаполнено(СпискиВедущейРоли) Тогда
		Возврат;
	КонецЕсли;
	
	Для Каждого ОписаниеСписка Из СпискиВедущейРоли Цикл
		Если ДобавленныеСписки.Получить(ОписаниеСписка.Ключ) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ДобавленныеСписки.Вставить(ОписаниеСписка.Ключ, Истина);
		Списки.Добавить(ОписаниеСписка.Ключ);
	КонецЦикла;

КонецПроцедуры

// Для вызова из модуля менеджера регистра ЗначенияГруппДоступа.
Процедура ЗапланироватьОбновлениеДоступаПриИзмененииРазрешенныхЗначений(ГруппыДоступаИТипыЗначений) Экспорт
	
	Описание = "ЗапланироватьОбновлениеДоступаПриИзмененииРазрешенныхЗначений";
	
	ЗапланироватьОбновлениеКэшаРасчетаПрав(Описание, "ЗначенияГруппДоступа");
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ГруппыДоступаИТипыЗначений", ГруппыДоступаИТипыЗначений);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ГруппыДоступаИТипыЗначений.ГруппаДоступа КАК ГруппаДоступа,
	|	ГруппыДоступаИТипыЗначений.ТипЗначенийДоступа КАК ТипЗначенийДоступа
	|ПОМЕСТИТЬ ГруппыДоступаИТипыЗначений
	|ИЗ
	|	&ГруппыДоступаИТипыЗначений КАК ГруппыДоступаИТипыЗначений
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ТаблицыГруппДоступа.Таблица КАК Таблица,
	|	ТИПЗНАЧЕНИЯ(ГруппыДоступаИТипыЗначений.ТипЗначенийДоступа) КАК ТипЗначенийДоступа
	|ИЗ
	|	РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ГруппыДоступаИТипыЗначений КАК ГруппыДоступаИТипыЗначений
	|		ПО ТаблицыГруппДоступа.ГруппаДоступа = ГруппыДоступаИТипыЗначений.ГруппаДоступа
	|ИТОГИ ПО
	|	Таблица";
	
	ТаблицыИТипыЗначенийДоступа = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	
	ЗапланироватьОбновлениеПользователейКлючейДоступа(ТаблицыИТипыЗначенийДоступа.Строки,
		Описание, Истина, Истина);
	
КонецПроцедуры

// Для процедуры ЗапланироватьОбновлениеДоступа, ЗапланироватьОбновлениеПараметровОграниченияДоступа.
Функция СлужебныйИдентификатор(ПолноеИмя)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПолноеИмя", ПолноеИмя);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ИдентификаторыОбъектовМетаданных.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ИдентификаторыОбъектовМетаданных КАК ИдентификаторыОбъектовМетаданных
	|ГДЕ
	|	ИдентификаторыОбъектовМетаданных.ПолноеИмя = &ПолноеИмя
	|
	|УПОРЯДОЧИТЬ ПО
	|	ИдентификаторыОбъектовМетаданных.ПометкаУдаления";
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Возврат Выборка.Ссылка;
	КонецЕсли;
	
	Возврат Null;
	
КонецФункции

// Для процедур ЗапланироватьОбновлениеДоступаПриИзмененииУчастниковГруппыДоступа,
// ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа,
// ЗапланироватьОбновлениеДоступаПриИзмененииТаблицГруппДоступа,
// ЗапланироватьОбновлениеДоступаПриИзмененииРазрешенныхЗначений.
//
Процедура ЗапланироватьОбновлениеПользователейКлючейДоступа(ОписаниеСписков, Описание, ДляПользователей,
				ДляВнешнихПользователей, ПриИзмененииУчастниковГруппыДоступа = Ложь, ВедущийОбъект = Неопределено)
	
	Если ОписаниеСписков.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Контекст = Новый Структура;
	Контекст.Вставить("ПриИзмененииУчастниковГруппыДоступа", ПриИзмененииУчастниковГруппыДоступа);
	Контекст.Вставить("ИдентификаторТранзакции",             Новый УникальныйИдентификатор);
	Контекст.Вставить("Список",                              Неопределено);
	Контекст.Вставить("ПолноеИмя",                           Неопределено);
	Контекст.Вставить("ТипыЗначенийДоступа",                 Неопределено);
	
	Если ТипЗнч(ОписаниеСписков) = Тип("ФиксированныйМассив") Тогда
		ИдентификаторыСписковПоПолнымИменам = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(ОписаниеСписков);
	Иначе
		ПолныеИмена = ОписаниеСписков.ВыгрузитьКолонку("Таблица");
		ОбъектыМетаданныхСписков = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(ПолныеИмена, Ложь);
	КонецЕсли;
	
	Списки = Новый Массив;
	СпискиДляПользователей = Новый Массив;
	СпискиДляВнешнихПользователей = Новый Массив;
	
	Для Каждого ОписаниеСписка Из ОписаниеСписков Цикл
		Если ИдентификаторыСписковПоПолнымИменам <> Неопределено Тогда
			Контекст.ПолноеИмя = ОписаниеСписка;
			Контекст.Список = ИдентификаторыСписковПоПолнымИменам.Получить(Контекст.ПолноеИмя);
		Иначе
			ОбъектМетаданных = ОбъектыМетаданныхСписков.Получить(ОписаниеСписка.Таблица);
			Контекст.ПолноеИмя = ?(ТипЗнч(ОбъектМетаданных) = Тип("ОбъектМетаданных"), ОбъектМетаданных.ПолноеИмя(), "");
			Контекст.Список    = ОписаниеСписка.Таблица;
			Если ТипЗнч(ОписаниеСписка) = Тип("СтрокаДереваЗначений") Тогда
				Контекст.ТипыЗначенийДоступа = ОписаниеСписка.Строки;
			КонецЕсли;
		КонецЕсли;
		Если Не ЗначениеЗаполнено(Контекст.ПолноеИмя) Тогда
			Продолжить;
		КонецЕсли;
		ЗапланироватьДляПользователей = ДляПользователей
			И ТребуетсяОбновлениеПользователейКлючейДоступа(Контекст, Ложь);
		
		ЗапланироватьДляВнешнихПользователей = ДляВнешнихПользователей
			И ТребуетсяОбновлениеПользователейКлючейДоступа(Контекст, Истина);
		
		Если ЗапланироватьДляПользователей И ЗапланироватьДляВнешнихПользователей Тогда
			Списки.Добавить(Контекст.ПолноеИмя);
			
		ИначеЕсли ЗапланироватьДляПользователей Тогда
			СпискиДляПользователей.Добавить(Контекст.ПолноеИмя);
			
		ИначеЕсли ЗапланироватьДляВнешнихПользователей Тогда
			СпискиДляВнешнихПользователей.Добавить(Контекст.ПолноеИмя);
		КонецЕсли;
	КонецЦикла;
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
	ПараметрыПланирования.КлючиДоступаКДанным = Ложь;
	ПараметрыПланирования.ВедущийОбъект = ВедущийОбъект;
	ПараметрыПланирования.Описание = Описание;
	ЗапланироватьОбновлениеДоступа(Списки, ПараметрыПланирования);
	
	ПараметрыПланирования.ДляВнешнихПользователей = Ложь;
	ЗапланироватьОбновлениеДоступа(СпискиДляПользователей, ПараметрыПланирования);
	
	ПараметрыПланирования.ДляПользователей = Ложь;
	ПараметрыПланирования.ДляВнешнихПользователей = Истина;
	ЗапланироватьОбновлениеДоступа(СпискиДляВнешнихПользователей, ПараметрыПланирования);
	
КонецПроцедуры

// Для процедуры ЗапланироватьОбновлениеПользователейКлючейДоступа.
Функция ТребуетсяОбновлениеПользователейКлючейДоступа(Контекст, ДляВнешнихПользователей)
	
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Контекст.ИдентификаторТранзакции, Неопределено, Ложь);
	Если ДляВнешнихПользователей Тогда
		ДополнительныйКонтекст = ДействующиеПараметры.ДополнительныйКонтекст.ДляВнешнихПользователей;
	Иначе
		ДополнительныйКонтекст = ДействующиеПараметры.ДополнительныйКонтекст.ДляПользователей;
	КонецЕсли;
	
	Если Не Контекст.ПриИзмененииУчастниковГруппыДоступа
	   И ДополнительныйКонтекст.СпискиСОтключеннымОграничением.Получить(Контекст.ПолноеИмя) <> Неопределено
	   И ДополнительныйКонтекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей.Получить(Контекст.ПолноеИмя) <> Неопределено Тогда
		Возврат Истина;
	КонецЕсли;
	
	СвойстваОграничения = ДополнительныйКонтекст.СвойстваОграниченияСписков.Получить(Контекст.ПолноеИмя);
	
	Если ДополнительныйКонтекст.СпискиСОтключеннымОграничением.Получить(Контекст.ПолноеИмя) <> Неопределено
	 Или СвойстваОграничения = Неопределено
	 Или СвойстваОграничения.ДоступЗапрещен
	 Или СвойстваОграничения.ПолеВладельца <> Неопределено
	   И Не СвойстваОграничения.ПолеВладельца.Отключено
	 Или Контекст.ПриИзмененииУчастниковГруппыДоступа
	   И Не СвойстваОграничения.РассчитыватьПраваПользователей Тогда
		
		Возврат Ложь;
	КонецЕсли;
	
	Если СвойстваОграничения <> Неопределено
	   И Контекст.ТипыЗначенийДоступа <> Неопределено
	   И Не Контекст.ПриИзмененииУчастниковГруппыДоступа Тогда
		
		СредиИзмененныхТиповЗначенийДоступаНетИспользуемых = Истина;
		ИспользуемыеТипыЗначенийДоступа = СвойстваОграничения.ИспользуемыеТипыЗначенийДоступа.Получить(); // Массив из Тип
		Для Каждого Строка Из Контекст.ТипыЗначенийДоступа Цикл
			Если ИспользуемыеТипыЗначенийДоступа.Найти(Строка.ТипЗначенийДоступа) <> Неопределено Тогда
				СредиИзмененныхТиповЗначенийДоступаНетИспользуемых = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если СредиИзмененныхТиповЗначенийДоступаНетИспользуемых Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Для процедуры ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа и
// модулей менеджера и объекта справочника ГруппыДоступа.
//
Процедура ЗапланироватьОбновлениеНаборовГруппДоступа(Описание, ДляПользователей = Истина, ДляВнешнихПользователей = Истина) Экспорт
	
	Если Не ДляПользователей И Не ДляВнешнихПользователей Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаКДанным);
	
	Список = "Справочник.НаборыГруппДоступа";
	КлючУникальности = Новый УникальныйИдентификатор;
	ИдентификаторСписка = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(Список);
	ТекущаяДата = ТекущаяДатаСеанса();
	МаксимальнаяДата = МаксимальнаяДата();
	
	// Обновление только по изменениям (не полное).
	ПараметрыЗадания = Новый Структура;
	УстановитьВидКлючаДанных(ПараметрыЗадания, "НовыеНаборыИзОдногоПользователя");
	ХранилищеПараметровЗадания = Новый ХранилищеЗначения(ПараметрыЗадания);
	
	Если ДляПользователей Тогда
		НоваяЗапись = НаборЗаписей.Добавить();
		НоваяЗапись.КлючУникальности                   = КлючУникальности;
		НоваяЗапись.Список                             = ИдентификаторСписка;
		НоваяЗапись.ДляВнешнихПользователей            = Ложь;
		НоваяЗапись.ДатаПоследнегоОбновленногоЭлемента = МаксимальнаяДата;
		НоваяЗапись.ПараметрыЗадания                   = ХранилищеПараметровЗадания;
		НоваяЗапись.РазмерЗадания                      = 3;
		НоваяЗапись.ДатаИзмененияЗаписиРегистра        = ТекущаяДата;
	КонецЕсли;
	
	Если ДляВнешнихПользователей Тогда
		НоваяЗапись = НаборЗаписей.Добавить();
		НоваяЗапись.КлючУникальности                   = КлючУникальности;
		НоваяЗапись.Список                             = ИдентификаторСписка;
		НоваяЗапись.ДляВнешнихПользователей            = Истина;
		НоваяЗапись.ДатаПоследнегоОбновленногоЭлемента = МаксимальнаяДата;
		НоваяЗапись.ПараметрыЗадания                   = ХранилищеПараметровЗадания;
		НоваяЗапись.РазмерЗадания                      = 3;
		НоваяЗапись.ДатаИзмененияЗаписиРегистра        = ТекущаяДата;
	КонецЕсли;
	
	НаборЗаписей.Записать(Ложь);
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
	ПараметрыПланирования.РазрешенныеКлючиДоступа = Ложь;
	ПараметрыПланирования.ДляПользователей        = ДляПользователей;
	ПараметрыПланирования.ДляВнешнихПользователей = ДляВнешнихПользователей;
	ПараметрыПланирования.Описание = Описание;
	
	СпискиПоИдентификаторам = Новый Соответствие;
	СпискиПоИдентификаторам.Вставить(ИдентификаторСписка, Список);
	
	ЗарегистрироватьПланированиеОбновленияДоступа(СпискиПоИдентификаторам, ПараметрыПланирования);
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Для процедур ЗапланироватьОбновлениеДоступаПриИзмененииУчастниковГруппыДоступа,
// ЗапланироватьОбновлениеДоступаПриКосвенномИзмененииУчастниковГруппыДоступа,
// ЗапланироватьОбновлениеДоступаПриИзмененииТаблицГруппДоступа,
// ЗапланироватьОбновлениеДоступаПриИзмененииРазрешенныхЗначений.
//
Процедура ЗапланироватьОбновлениеКэшаРасчетаПрав(Описание, ИмяИзмененныхДанных)
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
	ПараметрыПланирования.КлючиДоступаКДанным = Ложь;
	ПараметрыПланирования.ДляВнешнихПользователей = Ложь;
	ПараметрыПланирования.Описание = Описание;
	ПараметрыПланирования.ВедущийОбъект = Новый Структура("ПоДаннымКэшаРасчетаПрав", ИмяИзмененныхДанных);
	ЗапланироватьОбновлениеДоступа(СписокДляПланированияОбновленияКэшаРасчетаПрав(), ПараметрыПланирования);
	
КонецПроцедуры

// Для процедур ПодготовитьПланОбновления, ЗапланироватьОбновлениеКэшаРасчетаПрав
// и функции ВерсияДанныхДляКэшаРасчетаПрав.
//
Функция СписокДляПланированияОбновленияКэшаРасчетаПрав()
	
	Возврат "РегистрСведений.ПараметрыРаботыВерсийРасширений";
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * ТаблицыГруппДоступа       - Строка
//                                - Неопределено
//    * ЗначенияГруппДоступа      - Строка
//                                - Неопределено
//    * УчастникиГруппДоступа     - Строка
//                                - Неопределено
//    * СоставыГруппПользователей - Строка
//                                - Неопределено
//    * РолиПрофилейГруппДоступа  - Строка
//                                - Неопределено
//    * ГруппыДоступаПрофилей     - Строка
//                                - Неопределено
//
Функция НоваяВерсияДанныхДляКэшаРасчетаПрав(Версия = Неопределено) Экспорт
	
	НоваяВерсияДанных = Новый Структура;
	НоваяВерсияДанных.Вставить("ТаблицыГруппДоступа",       Версия);
	НоваяВерсияДанных.Вставить("ЗначенияГруппДоступа",      Версия);
	НоваяВерсияДанных.Вставить("УчастникиГруппДоступа",     Версия);
	НоваяВерсияДанных.Вставить("СоставыГруппПользователей", Версия);
	НоваяВерсияДанных.Вставить("РолиПрофилейГруппДоступа",  Версия);
	НоваяВерсияДанных.Вставить("ГруппыДоступаПрофилей",     Версия);
	
	Возврат НоваяВерсияДанных;
	
КонецФункции

// Для процедуры ОбработатьПланОбновленияКэшаРасчетаПрав.
Функция ИмяПараметраВерсииДанныхДляКэшаРасчетаПрав()
	
	Возврат "СтандартныеПодсистемы.УправлениеДоступом.ВерсияДанныхДляКэшаРасчетаПрав";
	
КонецФункции

#КонецОбласти

#Область ОбновлениеДоступа

// Запускает фоновое задание обновления доступа вместо регламентного задания.
//
// Параметры:
//  ЭтоЗапускВручную - Булево - если передать Ложь, то наименование будет начинаться с "Автозапуск",
//                              в противном случае, наименование будет начинаться с "Запуск вручную",
//                              блокировка обновления доступа будет снята, если была установлена,
//                              выполнение будет продолжаться до полного завершения.
//  ЭтоПерезапуск    - Булево - если передать Истина, тогда текущий сеанс фонового задания не будет
//                              считаться незавершенным исполнителем.
//
// Возвращаемое значение:
//  - Неопределено - обновление доступа не требуется или запрещено.
//  - Структура:
//   * УжеВыполняется - Булево - если обновление уже выполняется.
//
//   * ИдентификаторФоновогоЗадания - Неопределено - если обновление не выполняется или
//                                      выполняется не в фоновом задании.
//                                  - УникальныйИдентификатор - идентификатор фонового задания.
//
//   * СвойстваСеанса - Неопределено - если запуска не было или фоновое задание только добавлено в очередь.
//                    - Структура - со свойствами сеанса, если обновление уже выполняется:
//                        ** ИмяКомпьютера - Строка - одноименное свойства объекта СеансИнформационнойБазы.
//                        ** НомерСеанса   - Число  - одноименное свойства объекта СеансИнформационнойБазы.
//                        ** НачалоСеанса  - Строка - одноименное свойства объекта СеансИнформационнойБазы.
//
//   * ТекстПредупреждения - Неопределено - если запуска не было или запущено новое фоновое задание.
//                         - Строка - описание, что обновление доступа уже запущено.
//
Функция ЗапуститьОбновлениеДоступаНаУровнеЗаписей(ЭтоЗапускВручную = Ложь, ЭтоПерезапуск = Ложь) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Если Не ОграничиватьДоступНаУровнеЗаписейУниверсально(Истина) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Невозможно запустить обновление доступа на уровне записей, так как
			           |константа %1 выключена.'"),
			"ОграничиватьДоступНаУровнеЗаписейУниверсально");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ТранзакцияАктивна() Тогда
		ТекстОшибки =
			НСтр("ru = 'Невозможно запустить обновление доступа на уровне записей в открытой транзакции.'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Не ЭтоЗапускВручную И Не ЗапланированоОбновлениеДоступа() Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ПоследнееОбновлениеДоступа = ПоследнееОбновлениеДоступа();
	Если Не ЭтоЗапускВручную И ПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ИдентификаторИсключаемогоЗадания = Неопределено;
	Если ЭтоПерезапуск Тогда
		ТекущееФоновоеЗадание = ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание();
		Если ТекущееФоновоеЗадание <> Неопределено Тогда
			ИдентификаторИсключаемогоЗадания = ТекущееФоновоеЗадание.УникальныйИдентификатор;
		КонецЕсли;
	КонецЕсли;
	Результат = Новый Структура("УжеВыполняется, ТекстПредупреждения, ИдентификаторФоновогоЗадания, СвойстваСеанса", Истина, "");
	Исполнитель = ИсполнительОбновленияДоступа(ПоследнееОбновлениеДоступа, ИдентификаторИсключаемогоЗадания);
	
	Если Исполнитель = Неопределено Тогда
		Результат.УжеВыполняется = Ложь;
		Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
			Попытка
				// В файловой ИБ перезапуск фонового задания с расширениями базы данных
				// выполняется с той же версией динамического поколения конфигурации.
				СтандартныеПодсистемыСервер.ПроверитьДинамическоеОбновлениеВерсииПрограммы();
			Исключение
				Результат.ТекстПредупреждения = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
				Возврат Результат;
			КонецПопытки;
		КонецЕсли;
		Если ЭтоЗапускВручную Тогда
			УстановитьЗапретОбновленияДоступа(Ложь);
			ПараметрыЗадания = Новый Массив;
			ПараметрыЗадания.Добавить(Истина);
		Иначе
			ПараметрыЗадания = Неопределено;
		КонецЕсли;
		
		ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
		НаименованиеЗадания =
			?(ЭтоЗапускВручную,
				НСтр("ru = 'Запуск вручную'", ОбщегоНазначения.КодОсновногоЯзыка()),
				НСтр("ru = 'Автозапуск'", ОбщегоНазначения.КодОсновногоЯзыка()))
			+ ": " + Метаданные.РегламентныеЗадания.ОбновлениеДоступаНаУровнеЗаписей.Синоним + " ("
			+ СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'из сеанса %1 от %2'", ОбщегоНазначения.КодОсновногоЯзыка()),
				Формат(ТекущийСеанс.НомерСеанса, "ЧГ="),
				Формат(ТекущийСеанс.НачалоСеанса, "ДЛФ=DT")) + ")";
		
		ФоновоеЗадание = РасширенияКонфигурации.ВыполнитьФоновоеЗаданиеСРасширениямиБазыДанных(
			ИмяМетодаЗаданияОбновленияДоступа(), ПараметрыЗадания,, НаименованиеЗадания);
		
		Результат.ИдентификаторФоновогоЗадания = ФоновоеЗадание.УникальныйИдентификатор;
		
	ИначеЕсли ТипЗнч(Исполнитель) = Тип("ФоновоеЗадание")
	        И Исполнитель.УникальныйИдентификатор <> ПоследнееОбновлениеДоступа.ИдентификаторФоновогоЗадания Тогда
		
		Результат.ТекстПредупреждения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось начать обновление доступа, так как оно уже запущено %1 в %2'"),
			Формат(Исполнитель.Начало, "ДЛФ=D"),
			Формат(Исполнитель.Начало, "ДЛФ=T"));
	Иначе
		СвойстваСеанса = Новый Структура("ИмяКомпьютера, НомерСеанса, НачалоСеанса");
		Если ТипЗнч(Исполнитель) = Тип("ФоновоеЗадание") Тогда
			ЗаполнитьЗначенияСвойств(СвойстваСеанса, ПоследнееОбновлениеДоступа);
		Иначе
			ЗаполнитьЗначенияСвойств(СвойстваСеанса, Исполнитель);
		КонецЕсли;
		Результат.СвойстваСеанса = СвойстваСеанса;
		Результат.ТекстПредупреждения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Обновление доступа уже выполняется
			           |(компьютер: %1, сеанс: %2, начат: %3 в %4)'"),
			СвойстваСеанса.ИмяКомпьютера,
			СвойстваСеанса.НомерСеанса,
			Формат(СвойстваСеанса.НачалоСеанса, "ДЛФ=D"),
			Формат(СвойстваСеанса.НачалоСеанса, "ДЛФ=T"));
	КонецЕсли;
	
	Если ТипЗнч(Исполнитель) = Тип("ФоновоеЗадание") Тогда
		Результат.ИдентификаторФоновогоЗадания = Исполнитель.УникальныйИдентификатор;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Отменить обновление доступа, которое выполняется в фоновом задании.
Процедура ОтменитьОбновлениеДоступаНаУровнеЗаписей() Экспорт
	
	Исполнитель = ИсполнительОбновленияДоступа(ПоследнееОбновлениеДоступа());
	
	Если ТипЗнч(Исполнитель) = Тип("СеансИнформационнойБазы") Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Невозможно отменить полное обновление доступа
			           |(компьютер: %1, сеанс: %2, начат: %3 в %4)'"),
			Исполнитель.ИмяКомпьютера,
			Исполнитель.НомерСеанса,
			Формат(Исполнитель.НачалоСеанса, "ДЛФ=D"),
			Формат(Исполнитель.НачалоСеанса, "ДЛФ=T"));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Выполняется = ТипЗнч(Исполнитель) = Тип("ФоновоеЗадание");
	
	ЗавершитьПотокиОбновленияДоступа(Истина);
	УстановитьЗапретОбновленияДоступа(Истина, Выполняется);
	
	Если Выполняется Тогда
		Исполнитель = Исполнитель.ОжидатьЗавершенияВыполнения(СекундОжиданияЗавершенияФоновогоЗадания());
		Если Исполнитель.Состояние = СостояниеФоновогоЗадания.Активно Тогда
			Исполнитель.Отменить();
		КонецЕсли;
	КонецЕсли;
	
	Отбор = Новый Структура;
	Отбор.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);
	Отбор.Вставить("ИмяМетода", ИмяМетодаЗаданияОбновленияДоступа());
	ОсновныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
	
	Для Каждого ОсновноеЗадание Из ОсновныеЗадания Цикл
		ОсновноеЗадание = ОсновноеЗадание.ОжидатьЗавершенияВыполнения(1);
		Если ОсновноеЗадание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
			ОсновноеЗадание.Отменить();
		КонецЕсли;
	КонецЦикла;
	
	ОтменитьФоновыеЗаданияПотоковОбновленияДоступа();
	
КонецПроцедуры

// Для процедуры УстановитьОбновлениеДоступа.
Процедура ВключитьРегламентноеЗаданиеОбновленияДоступа() Экспорт
	
	ИзменитьРегламентноеЗаданиеОбновленияДоступа(Истина);
	
КонецПроцедуры

// Для процедуры УстановитьОбновлениеДоступа.
Процедура ОтключитьРегламентноеЗаданиеОбновленияДоступа() Экспорт
	
	ИзменитьРегламентноеЗаданиеОбновленияДоступа(Ложь);
	
КонецПроцедуры

// Для процедуры УстановитьОбновлениеДоступа.
Процедура ИзменитьРегламентноеЗаданиеОбновленияДоступа(ВключитьЗадание)
	
	Отбор = Новый Структура("Метаданные", Метаданные.РегламентныеЗадания.ОбновлениеДоступаНаУровнеЗаписей);
	
	ТребуетсяИзменить = Ложь;
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		ТребуетсяИзменить = Истина;
	Иначе
		УстановитьПривилегированныйРежим(Истина);
		Задания = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
		Для Каждого Задание Из Задания Цикл
			Если ВключитьЗадание <> Задание.Использование Тогда
				ТребуетсяИзменить = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		УстановитьПривилегированныйРежим(Ложь);
	КонецЕсли;
	
	Если Не ТребуетсяИзменить Тогда
		Возврат;
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("Константа.КоличествоПотоковОбновленияДоступа");
	
	ЭтоОшибкаБлокировки = Ложь;
	НомерПопыткиБлокировки = 0;
	Пока Истина Цикл
		НомерПопыткиБлокировки = НомерПопыткиБлокировки + 1;
		НачатьТранзакцию();
		Попытка
			ЭтоОшибкаБлокировки = Истина;
			Блокировка.Заблокировать();
			ЭтоОшибкаБлокировки = Ложь;
			УстановитьПривилегированныйРежим(Истина);
			Задания = РегламентныеЗаданияСервер.НайтиЗадания(Отбор);
			Для Каждого Задание Из Задания Цикл
				Если ВключитьЗадание = Задание.Использование Тогда
					Продолжить;
				КонецЕсли;
				РегламентныеЗаданияСервер.ИзменитьЗадание(Задание,
					Новый Структура("Использование", ВключитьЗадание));
			КонецЦикла;
			УстановитьПривилегированныйРежим(Ложь);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			Если ЭтоОшибкаБлокировки
			   И НомерПопыткиБлокировки <= 3
			   И Не ТранзакцияАктивна() Тогда
				Продолжить;
			КонецЕсли;
			ВызватьИсключение;
		КонецПопытки;
		Прервать;
	КонецЦикла;
	
КонецПроцедуры

// Включает или отключает запрет обновления доступа при запуске
// с помощью регламентного задания или при программном запуске.
//
// При вызове процедуры ОбновлениеДоступаНаУровнеЗаписей
// с признаком ОбновитьВсе запрет игнорируется.
// 
// При вызове процедуры ЗапуститьОбновлениеДоступаНаУровнеЗаписей
// с признаком ЭтоЗапускВручную запрет снимается автоматически.
//
Процедура УстановитьЗапретОбновленияДоступа(Использование, ОтменитьОбновление = Ложь) Экспорт
	
	БлокировкаДанных = Новый БлокировкаДанных;
	БлокировкаДанных.Добавить("Константа.ПоследнееОбновлениеДоступа");
	
	ЕстьВнешняяТранзакция = ТранзакцияАктивна();
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		ЗначениеКонстанты = Константы.ПоследнееОбновлениеДоступа.Получить();
		ПоследнееОбновлениеДоступа = ПоследнееОбновлениеДоступа(ЗначениеКонстанты);
		
		Записать = Ложь;
		Если ПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено <> Использование Тогда
			ПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено = Использование;
			Записать = Истина;
		КонецЕсли;
		
		Если ОтменитьОбновление
		   И ПоследнееОбновлениеДоступа.ДатаЗапускаНаСервере > ПоследнееОбновлениеДоступа.ДатаЗавершенияНаСервере
		   И ( Не ПоследнееОбновлениеДоступа.ОбновлениеОтменено
		      Или ПоследнееОбновлениеДоступа.ТекстОшибкиЗавершения <> "") Тогда
			
			ПоследнееОбновлениеДоступа.ОбновлениеОтменено = Истина;
			ПоследнееОбновлениеДоступа.ТекстОшибкиЗавершения = "";
			ПоследнееОбновлениеДоступа.ДатаПолногоЗавершения = '00010101';
			ПоследнееОбновлениеДоступа.ДатаЗавершенияНаСервере = ТекущаяДатаНаСервере();
			ПоследнееОбновлениеДоступа.ПоследнееВыполнениеСекунд =
				ПоследнееОбновлениеДоступа.ДатаЗавершенияНаСервере - ПоследнееОбновлениеДоступа.ДатаЗапускаНаСервере
					+ СекундОжиданияЗавершенияФоновогоЗадания() + 1;
			Записать = Истина;
		КонецЕсли;
		
		Если Записать Тогда
			УстановитьПоследнееОбновлениеДоступа(ПоследнееОбновлениеДоступа, ЕстьВнешняяТранзакция);
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедур ОтменитьОбновлениеДоступаНаУровнеЗаписей, УстановитьЗапретОбновленияДоступа.
Функция СекундОжиданияЗавершенияФоновогоЗадания()
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		Возврат 3;
	Иначе
		Возврат 15;
	КонецЕсли;
	
КонецФункции

// Обработчик регламентного задания ОбновлениеДоступаНаУровнеЗаписей.
Процедура ОбновлениеДоступаНаУровнеЗаписей(ОбновитьВсе = Ложь, ВызыватьИсключениеВместоРегистрацииОшибки = Ложь) Экспорт
	
	// Регламентное задание должно запускаться сразу, как только было запланировано обновление доступа.
	// После обработки первой порции по всем таблицам (2 мин на все) можно сделать
	// небольшую паузу вместо непрерывного выполнения до конца.
	// В данном случае пауза - это перезапуск через 15 секунд.
	// В отличие от обычных регламентных заданий это задание самоотключаемое,
	// то есть после отработки запланированного обновления, задание выключает само себя и
	// запуски полностью прекращаются до того, как будет запланировано новое обновление доступа.
	
	ОбщегоНазначения.ПриНачалеВыполненияРегламентногоЗадания(
		Метаданные.РегламентныеЗадания.ОбновлениеДоступаНаУровнеЗаписей);
	
	ВыполнитьОбновлениеДоступаНаУровнеЗаписей(ОбновитьВсе, ВызыватьИсключениеВместоРегистрацииОшибки, 0);
	
КонецПроцедуры

// Выполняет обновление доступа, если запланировано.
Процедура ВыполнитьОбновлениеДоступаНаУровнеЗаписей(ОбновитьВсе,
			ВызыватьИсключениеВместоРегистрацииОшибки, СекундНеБолее, ПослеОбновлениеИБ = Ложь)
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
	ОписаниеОсновногоСеанса = ОписаниеОсновногоСеанса();
	ОписаниеОсновногоСеанса.Вставить("НомерСеанса",  ТекущийСеанс.НомерСеанса);
	ОписаниеОсновногоСеанса.Вставить("НачалоСеанса", ТекущийСеанс.НачалоСеанса);
	
	Если ТекущийСеанс.ИмяПриложения <> "BackgroundJob" Тогда
		Если ОбновитьВсе Тогда
			ИдентификаторФоновогоЗадания = ИдентификаторПроизвольногоСеанса();
		Иначе
			ТекстОшибки = НСтр("ru = 'Обновление доступа может выполняться порциями только в фоновом задании.'");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ИдентификаторОсновногоСеанса = Строка(Новый УникальныйИдентификатор);
	Иначе
		ТекущееФоновоеЗадание = ТекущийСеанс.ПолучитьФоновоеЗадание();
		Если ТекущееФоновоеЗадание = Неопределено Тогда
			ТекстОшибки = НСтр("ru = 'Не удалось получить фоновое задание текущего сеанса.'");
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ИдентификаторФоновогоЗадания = ТекущееФоновоеЗадание.УникальныйИдентификатор;
		ОписаниеОсновногоСеанса.ФоновоеЗадание = ТекущееФоновоеЗадание;
		ОписаниеОсновногоСеанса.ИдентификаторФоновогоЗадания = ИдентификаторФоновогоЗадания;
		ИдентификаторОсновногоСеанса = Строка(ИдентификаторФоновогоЗадания);
	КонецЕсли;
	ОписаниеОсновногоСеанса.Вставить("Идентификатор", ИдентификаторОсновногоСеанса
		+ " (" + НСтр("ru = 'Идентификатор основного сеанса'") + ")");
	
	БлокировкаДанных = Новый БлокировкаДанных;
	БлокировкаДанных.Добавить("Константа.ПоследнееОбновлениеДоступа");
	
	ЕстьВнешняяТранзакция = ТранзакцияАктивна();
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		ЗначениеКонстанты = Константы.ПоследнееОбновлениеДоступа.Получить();
		ПоследнееОбновлениеДоступа = ПоследнееОбновлениеДоступа(ЗначениеКонстанты);
		
		Если ОбновитьВсе Или Не ПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено Тогда
			Исполнитель = ИсполнительОбновленияДоступа(ПоследнееОбновлениеДоступа, ИдентификаторФоновогоЗадания);
			
			Если Исполнитель = Неопределено Тогда
				ПоследнееОбновлениеДоступа.ОбновлениеОтменено   = Ложь;
				ПоследнееОбновлениеДоступа.ДатаЗапускаНаСервере = ТекущаяДатаНаСервере();
				ПоследнееОбновлениеДоступа.НомерСеанса          = ТекущийСеанс.НомерСеанса;
				ПоследнееОбновлениеДоступа.НачалоСеанса         = ТекущийСеанс.НачалоСеанса;
				ПоследнееОбновлениеДоступа.ИмяКомпьютера        = ТекущийСеанс.ИмяКомпьютера;
				ПоследнееОбновлениеДоступа.ИдентификаторФоновогоЗадания = ИдентификаторФоновогоЗадания;
				
				УстановитьПоследнееОбновлениеДоступа(ПоследнееОбновлениеДоступа, ЕстьВнешняяТранзакция);
			КонецЕсли;
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если Не ОбновитьВсе И ПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено Тогда
		Если ТекущееФоновоеЗадание.РегламентноеЗадание <> Неопределено Тогда
			УстановитьОбновлениеДоступа(Ложь);
		КонецЕсли;
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Обновление доступа запрещено.
			           |Чтобы разрешить нужно нажать ""Разрешить"" в форме ""Обновление доступа на уровне записей"".
			           |Форму можно открыть из панели ""Настройки пользователей и прав"" или перейти по навигационной ссылке:
			           |%1'"),
			"e1cib/app/РегистрСведений.ОбновлениеКлючейДоступаКДанным.Форма.ОбновлениеДоступаНаУровнеЗаписей");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Исполнитель <> Неопределено Тогда
		Если Не ОбновитьВсе Или ПослеОбновлениеИБ Тогда
			Возврат;
		КонецЕсли;
		
		Если ТипЗнч(Исполнитель) = Тип("ФоновоеЗадание")
		   И Исполнитель.УникальныйИдентификатор <> ПоследнееОбновлениеДоступа.ИдентификаторФоновогоЗадания Тогда
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось начать обновление доступа, так как оно уже запущено %1 в %2'"),
				Формат(Исполнитель.Начало, "ДЛФ=D"),
				Формат(Исполнитель.Начало, "ДЛФ=T"));
		Иначе
			СвойстваСеанса = ?(ТипЗнч(Исполнитель) = Тип("ФоновоеЗадание"),
				ПоследнееОбновлениеДоступа, Исполнитель);
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось начать обновление доступа, так как оно уже выполняется
				           |(компьютер: %1, сеанс: %2, начат: %3 в %4)'"),
				СвойстваСеанса.ИмяКомпьютера,
				СвойстваСеанса.НомерСеанса,
				Формат(СвойстваСеанса.НачалоСеанса, "ДЛФ=D"),
				Формат(СвойстваСеанса.НачалоСеанса, "ДЛФ=T"));
		КонецЕсли;
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ПоследнееОбновлениеДоступа.ОбновлениеОтменено = Ложь;
	ПоследнееОбновлениеДоступа.ТекстОшибкиЗавершения = "";
	ПоследнееОбновлениеДоступа.ИдентификаторФоновогоЗадания =
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
	
	ПерезапускВозможен = ТекущееФоновоеЗадание <> Неопределено
		И Не ОбновитьВсе
		И Не ВызыватьИсключениеВместоРегистрацииОшибки
		И СекундНеБолее = 0
		И Не ПослеОбновлениеИБ;
	
	ТекстОшибкиПланирования = "";
	ТекстВсехОшибок = "";
	ТекстОшибкиЗавершения = "";
	ДатаПолногоЗавершения = ПоследнееОбновлениеДоступа.ДатаПолногоЗавершения;
	Попытка
		Если ОграничиватьДоступНаУровнеЗаписейУниверсально() Тогда
			ПроверитьОбновитьДействующиеПараметрыОграниченияДоступа();
			ЗапланироватьОбработкуУстаревшихЭлементов(ТекстОшибкиПланирования,
				ПоследнееОбновлениеДоступа.ПоследнееПланированиеОбработкиУстаревшихЭлементов);
			ВыполнитьОбновлениеДоступа(ОбновитьВсе, ОписаниеОсновногоСеанса,
				ПоследнееОбновлениеДоступа.ОбновлениеОтменено, ТекстОшибкиЗавершения, СекундНеБолее,
				ПоследнееОбновлениеДоступа.ДатаПолногоЗавершения);
			Если Не ПерезапускВозможен Тогда
				СтандартныеПодсистемыСервер.ТребуетсяПерезапускСеанса(ТекстВсехОшибок);
			КонецЕсли;
		Иначе
			УстановитьОбновлениеДоступа(Ложь);
		КонецЕсли;
	Исключение
		Если ТранзакцияАктивна() Тогда
			ВызватьИсключение;
		КонецЕсли;
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстВсехОшибок = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		Если ПерезапускВозможен
		   И СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке) Тогда
			ТекстВсехОшибок = "";
		КонецЕсли;
	КонецПопытки;
	
	Попытка
		ЗавершитьПотокиОбновленияДоступа();
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ДобавитьТекстОшибкиЗавершения(ТекстВсехОшибок, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось завершить потоки обновления доступа по причине:
			           |%1'"),
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)));
	КонецПопытки;
	
	Попытка
		ОтменитьФоновыеЗаданияПотоковОбновленияДоступа(2);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ДобавитьТекстОшибкиЗавершения(ТекстВсехОшибок, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось отменить фоновые задания потоков обновления доступа по причине:
			           |%1'"),
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке)));
	КонецПопытки;
	
	ДобавитьТекстОшибкиЗавершения(ТекстВсехОшибок, ТекстОшибкиПланирования);
	ДобавитьТекстОшибкиЗавершения(ТекстВсехОшибок, ТекстОшибкиЗавершения);
	
	ПоследнееОбновлениеДоступа.ДатаЗавершенияНаСервере = ТекущаяДатаНаСервере();
	Если ЗначениеЗаполнено(ТекстВсехОшибок) Тогда
		ПоследнееОбновлениеДоступа.ДатаПолногоЗавершения = '00010101';
	КонецЕсли;
	Если Не ЗначениеЗаполнено(ДатаПолногоЗавершения)
	 Или ПоследнееОбновлениеДоступа.ДатаПолногоЗавершения <> ДатаПолногоЗавершения Тогда
	
		ПоследнееОбновлениеДоступа.ТекстОшибкиЗавершения = ТекстВсехОшибок;
		ПоследнееОбновлениеДоступа.ПоследнееВыполнениеСекунд =
			ПоследнееОбновлениеДоступа.ДатаЗавершенияНаСервере - ПоследнееОбновлениеДоступа.ДатаЗапускаНаСервере;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		ЗначениеКонстанты = Константы.ПоследнееОбновлениеДоступа.Получить();
		ТекущееПоследнееОбновлениеДоступа = ПоследнееОбновлениеДоступа(ЗначениеКонстанты);
		
		ПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено =
			ТекущееПоследнееОбновлениеДоступа.ОбновлениеДоступаЗапрещено;
		
		УстановитьПоследнееОбновлениеДоступа(ПоследнееОбновлениеДоступа, ЕстьВнешняяТранзакция);
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если ПерезапускВозможен И СтандартныеПодсистемыСервер.ТребуетсяПерезапускСеанса() Тогда
		ЗапуститьОбновлениеДоступаНаУровнеЗаписей(, Истина);
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ТекстВсехОшибок) Тогда
		Возврат;
	КонецЕсли;
	
	ТекстВсехОшибок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Не удалось выполнить обновление доступа по причине:
		           |%1'"), ТекстВсехОшибок);
	
	Если ВызыватьИсключениеВместоРегистрацииОшибки Тогда
		ВызватьИсключение ТекстВсехОшибок;
	Иначе
		ЗарегистрироватьОшибкуОбновленияДоступа(ТекстВсехОшибок,
			Новый Структура("ОписаниеОсновногоСеанса", ОписаниеОсновногоСеанса));
	КонецЕсли;
	
КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//     * ИдентификаторФоновогоЗадания - УникальныйИдентификатор
//     * ФоновоеЗадание               - ФоновоеЗадание
//     * НомерСеанса                  - Число
//     * НачалоСеанса                 - Дата
//     * Идентификатор                - Строка - представление сеанса
//
Функция ОписаниеОсновногоСеанса()
	
	Возврат Новый Структура("ИдентификаторФоновогоЗадания, ФоновоеЗадание");
	
КонецФункции

// Для функции ЗапуститьОбновлениеДоступаНаУровнеЗаписей.
Функция ЗапланированоОбновлениеДоступа()
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК ОбновлениеКлючейДоступаКДанным
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаПользователей КАК ОбновлениеКлючейДоступаПользователей";
	
	Возврат Не Запрос.Выполнить().Пустой();
	
КонецФункции

// Для функции ЗапуститьОбновлениеДоступаНаУровнеЗаписей и
// процедур ОбновлениеДоступаНаУровнеЗаписей, ОтменитьОбновлениеДоступаНаУровнеЗаписей и
// формы ОбновлениеДоступаНаУровнеЗаписей.
//
// Возвращаемое значение:
//  Структура:
//   * ДатаЗапускаНаСервере - Дата
//   * ДатаЗавершенияНаСервере - Дата
//   * ОбновлениеОтменено - Булево
//   * ПоследнееВыполнениеСекунд - Число
//   * ДатаПолногоЗавершения - Дата
//   * ТекстОшибкиЗавершения - Строка
//   * НомерСеанса - Число
//   * ИмяКомпьютера - Строка
//   * ОбновлениеДоступаЗапрещено - Булево
//   * БалансировкаНагрузкиНаДиск - Булево
//   * ИдентификаторФоновогоЗадания - УникальныйИдентификатор
//   * ПоследнееПланированиеОбработкиУстаревшихЭлементов - Дата
//
Функция ПоследнееОбновлениеДоступа(ТекущееЗначение = Неопределено) Экспорт
	
	Если ТекущееЗначение = Неопределено Тогда
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ПоследнееОбновлениеДоступа.Значение КАК Значение
		|ИЗ
		|	Константа.ПоследнееОбновлениеДоступа КАК ПоследнееОбновлениеДоступа";
		
		Выборка = Запрос.Выполнить().Выбрать();
		ТекущееЗначение = ?(Выборка.Следующий(), Выборка.Значение, Неопределено);
	КонецЕсли;
	
	Свойства = Новый Структура;
	Свойства.Вставить("ДатаЗапускаНаСервере",      '00010101');
	Свойства.Вставить("ДатаЗавершенияНаСервере",   '00010101');
	Свойства.Вставить("ОбновлениеОтменено",        Ложь);
	Свойства.Вставить("ПоследнееВыполнениеСекунд", 0);
	Свойства.Вставить("ДатаПолногоЗавершения",     '00010101');
	Свойства.Вставить("ТекстОшибкиЗавершения",     "");
	Свойства.Вставить("НомерСеанса",               0);
	Свойства.Вставить("НачалоСеанса",              '00010101');
	Свойства.Вставить("ИмяКомпьютера",             "");
	Свойства.Вставить("ОбновлениеДоступаЗапрещено", Ложь);
	Свойства.Вставить("БалансировкаНагрузкиНаДиск", Истина);
	Свойства.Вставить("ИдентификаторДоступа");
	Свойства.Вставить("ИдентификаторФоновогоЗадания",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	Свойства.Вставить("ПоследнееПланированиеОбработкиУстаревшихЭлементов", '00010101');
	
	Если ТипЗнч(ТекущееЗначение) <> Тип("ХранилищеЗначения") Тогда
		Возврат Свойства;
	КонецЕсли;
	
	ТекущиеСвойства = ТекущееЗначение.Получить();
	
	Если ТипЗнч(ТекущиеСвойства) <> Тип("Структура") Тогда
		Возврат Свойства;
	КонецЕсли;
	
	ЗаполнитьЗначенияСвойств(Свойства, ТекущиеСвойства);
	
	Возврат Свойства;
	
КонецФункции

// Для процедур ВыполнитьОбновлениеДоступаНаУровнеЗаписей,
// УстановитьЗапретОбновленияДоступа и УстановитьБалансировкуНагрузкиНаДиск.
// 
Процедура УстановитьПоследнееОбновлениеДоступа(Свойства, ЕстьВнешняяТранзакция)
	
	Если Не ЗначениеЗаполнено(Свойства.ИдентификаторДоступа) И Не ЕстьВнешняяТранзакция Тогда
		Свойства.ИдентификаторДоступа = Новый УникальныйИдентификатор;
	КонецЕсли;
	
	МенеджерЗначения = СлужебныйМенеджерЗначения(Константы.ПоследнееОбновлениеДоступа);
	МенеджерЗначения.Значение = Новый ХранилищеЗначения(Свойства);
	МенеджерЗначения.Записать();
	
КонецПроцедуры

// Для константы ОграничиватьДоступНаУровнеЗаписейУниверсально.
Процедура ОчиститьПоследнееОбновлениеДоступа() Экспорт
	
	Свойства = ПоследнееОбновлениеДоступа(Null);
	ЗаполнитьЗначенияСвойств(Свойства, ПоследнееОбновлениеДоступа(),
		"ИдентификаторДоступа, ОбновлениеДоступаЗапрещено, БалансировкаНагрузкиНаДиск");
	
	МенеджерЗначения = СлужебныйМенеджерЗначения(Константы.ПоследнееОбновлениеДоступа);
	МенеджерЗначения.Значение = Новый ХранилищеЗначения(Свойства);
	МенеджерЗначения.Записать();
	
КонецПроцедуры

// Для функции ЗапуститьОбновлениеДоступаНаУровнеЗаписей и
// процедур ОбновлениеДоступаНаУровнеЗаписей, ОтменитьОбновлениеДоступаНаУровнеЗаписей и
// формы ОбновлениеДоступаНаУровнеЗаписей.
//
// Параметры:
//  ПоследнееОбновлениеДоступа - см. ПоследнееОбновлениеДоступа
//  ИдентификаторИсключаемогоЗадания - УникальныйИдентификатор
//                                   - Неопределено
// 
// Возвращаемое значение:
//  - ФоновоеЗадание
//  - СеансИнформационнойБазы
//  - Неопределено
//
Функция ИсполнительОбновленияДоступа(ПоследнееОбновлениеДоступа, ИдентификаторИсключаемогоЗадания = Неопределено) Экспорт
	
	Если ЗначениеЗаполнено(ПоследнееОбновлениеДоступа.ИдентификаторФоновогоЗадания) Тогда
		Если ПоследнееОбновлениеДоступа.ИдентификаторФоновогоЗадания = ИдентификаторПроизвольногоСеанса() Тогда
			Сеансы = ПолучитьСеансыИнформационнойБазы();
			Для Каждого Сеанс Из Сеансы Цикл
				Если Сеанс.НомерСеанса  = ПоследнееОбновлениеДоступа.НомерСеанса
				   И Сеанс.НачалоСеанса = ПоследнееОбновлениеДоступа.НачалоСеанса Тогда
					Возврат Сеанс;
				КонецЕсли;
			КонецЦикла;
		Иначе
			ИсполняющееФоновоеЗадание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(
				ПоследнееОбновлениеДоступа.ИдентификаторФоновогоЗадания);
		
			Если ИсполняющееФоновоеЗадание <> Неопределено
			   И ИсполняющееФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
				
				Возврат ИсполняющееФоновоеЗадание;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Отбор = Новый Структура("Состояние, ИмяМетода", СостояниеФоновогоЗадания.Активно,
		ИмяМетодаЗаданияОбновленияДоступа());
	
	НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
	Для Каждого НайденноеЗадание Из НайденныеЗадания Цикл
		Если НайденноеЗадание.УникальныйИдентификатор <> ИдентификаторИсключаемогоЗадания Тогда
			Возврат НайденноеЗадание;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Неопределено;
	
КонецФункции

// Для процедуры ОбновлениеДоступаНаУровнеЗаписей, ИсполнительОбновленияДоступа.
Функция ИдентификаторПроизвольногоСеанса()
	
	Возврат Новый УникальныйИдентификатор("ba4730f7-0493-402d-b5d3-8052c80fb125");
	
КонецФункции

// Для процедур ОбновлениеДоступаНаУровнеЗаписей, ДобавитьЗаданияОбновленияДоступа.
Процедура ПроверитьОбновитьДействующиеПараметрыОграниченияДоступа()
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	РегистрыСведений.ИспользуемыеВидыДоступа.ОбновитьДанныеРегистра(, Истина);
	
	ДатаОбновленияВерсииРасширений = Справочники.ВерсииРасширений.ПоследняяВерсияРасширений().ДатаОбновления;
	
	ДатаОбновленияВсехПараметровРаботыПрограммы =
		РегистрыСведений.ПараметрыРаботыПрограммы.ДатаОбновленияВсехПараметровРаботыПрограммы();
	
	ДатаПоследнегоЗаполненияВсехПараметровРаботыРасширений =
		РегистрыСведений.ПараметрыРаботыВерсийРасширений.ДатаПоследнегоЗаполненияВсехПараметровРаботыРасширений();
	
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь);
	ДатаСозданияПараметровОграниченияДоступа = ПараметрыСеанса.ПараметрыОграниченияДоступа.ДатаСоздания;
	
	ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ДатаПроверкиПараметровОграниченияДоступа";
	ДатаПроверкиПараметровОграниченияДоступа = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
	Если ТипЗнч(ДатаПроверкиПараметровОграниченияДоступа) <> Тип("Дата") Тогда
		ДатаПроверкиПараметровОграниченияДоступа = '00010101';
	КонецЕсли;
	
	Если ДатаПроверкиПараметровОграниченияДоступа > ДатаСозданияПараметровОграниченияДоступа Тогда
		ДатаАктуальностиПараметровОграниченияДоступа = ДатаПроверкиПараметровОграниченияДоступа;
	Иначе
		ДатаАктуальностиПараметровОграниченияДоступа = ДатаСозданияПараметровОграниченияДоступа;
	КонецЕсли;
	
	ИспользованиеВидовДоступаИзменено = ИспользованиеВидовДоступаИзменено(ДействующиеПараметры);
	
	Если ДатаОбновленияВерсииРасширений                         > ДатаАктуальностиПараметровОграниченияДоступа
	 Или ДатаОбновленияВсехПараметровРаботыПрограммы            > ДатаАктуальностиПараметровОграниченияДоступа
	 Или ДатаПоследнегоЗаполненияВсехПараметровРаботыРасширений > ДатаАктуальностиПараметровОграниченияДоступа
	 Или ИспользованиеВидовДоступаИзменено Тогда
		
		Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
			// Предварительное кэширование в файловой ИБ вне транзакции для предотвращения конфликтов блокировок.
			ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.УправлениеДоступом"); // СтандартныеПодсистемыПовтИсп.ИменаПодсистем
			УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписей();
			УправлениеДоступомСлужебныйПовтИсп.КонстантаОграничиватьДоступНаУровнеЗаписейУниверсально();
		КонецЕсли;
		
		Если ИспользованиеВидовДоступаИзменено Тогда
			Попытка
				РегистрыСведений.ИспользуемыеВидыДоступа.ПриИзмененииИспользованияВидовДоступа();
			Исключение
				РегистрыСведений.ИспользуемыеВидыДоступа.ПриИзмененииИспользованияВидовДоступа();
			КонецПопытки;
		КонецЕсли;
		Попытка
			ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений();
		Исключение
			ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений();
		КонецПопытки;
		Если ПараметрыСеанса.ПараметрыОграниченияДоступа.Версия > 100000000000000 Тогда
			Попытка
				УменьшитьНомераВерсийПараметровОграниченияДоступа();
			Исключение
				УменьшитьНомераВерсийПараметровОграниченияДоступа();
			КонецПопытки;
		КонецЕсли;
		НоваяДатаПроверкиПараметровОграниченияДоступа = ТекущаяДатаСеанса();
		Попытка
			ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Истина);
		Исключение
			ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Истина);
		КонецПопытки;
		Попытка
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра,
				НоваяДатаПроверкиПараметровОграниченияДоступа, Истина);
		Исключение
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра,
				НоваяДатаПроверкиПараметровОграниченияДоступа, Истина);
		КонецПопытки;
	КонецЕсли;
	
	// Удаление устаревших параметров ограничения доступа.
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ТекущаяВерсия",   ПараметрыСеанса.ПараметрыОграниченияДоступа.Версия);
	Запрос.УстановитьПараметр("ДатаУстаревания", ТекущаяДатаСеанса() - 2 * 24 * 60 *60);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ПараметрыОграниченияДоступа.Версия КАК Версия,
	|	ПараметрыОграниченияДоступа.ДатаСоздания КАК ДатаСоздания
	|ИЗ
	|	РегистрСведений.ПараметрыОграниченияДоступа КАК ПараметрыОграниченияДоступа
	|ГДЕ
	|	ПараметрыОграниченияДоступа.Версия < &ТекущаяВерсия
	|	И ПараметрыОграниченияДоступа.ДатаСоздания <= &ДатаУстаревания
	|
	|УПОРЯДОЧИТЬ ПО
	|	Версия УБЫВ,
	|	ДатаСоздания УБЫВ";
	
	// АПК:1328-выкл. См. 648.1.1. Допустимо чтение без предварительной
	// управляемой разделяемой блокировки, так как используется только
	// для очистки и какой сеанс очистит первым не важно.
	// Если установить разделяемую блокировку, то позже будет повышение
	// до исключительной, что ведет к взаимоблокировке и недопустимо.
	// Если установить исключительную блокировку на таблицу, то
	// это вызовет избыточное замедление запуска сеансов при установке
	// параметров сеанса из параметров ограничения, что недопустимо.
	РезультатЗапроса = Запрос.Выполнить();
	// АПК:1328-вкл.
	Если РезультатЗапроса.Пустой() Тогда
		Возврат;
	КонецЕсли;
	
	НаборЗаписей = РегистрыСведений.ПараметрыОграниченияДоступа.СоздатьНаборЗаписей();
	
	Выборка = РезультатЗапроса.Выбрать();
	Пока Выборка.Следующий() Цикл
		НаборЗаписей.Отбор.Версия.Установить(Выборка.Версия);
		Попытка
			НаборЗаписей.Записать();
		Исключение
			НаборЗаписей.Записать();
		КонецПопытки;
	КонецЦикла;
	
	УстановитьПривилегированныйРежим(Ложь);
	УстановитьОтключениеБезопасногоРежима(Ложь);
	
КонецПроцедуры

// Для процедуры ПроверитьОбновитьДействующиеПараметрыОграниченияДоступа.
Процедура УменьшитьНомераВерсийПараметровОграниченияДоступа()
	
	Блокировка = Новый БлокировкаДанных;
	Блокировка.Добавить("РегистрСведений.ПараметрыОграниченияДоступа");
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		НаборЗаписей = РегистрыСведений.ПараметрыОграниченияДоступа.СоздатьНаборЗаписей();
		НаборЗаписей.Прочитать();
		Выгрузка = НаборЗаписей.Выгрузить();
		Выгрузка.Сортировать("Версия");
		ТекущаяВерсия = 1;
		Для Каждого Строка Из Выгрузка Цикл
			Строка.Версия = ТекущаяВерсия;
			ТекущаяВерсия = ТекущаяВерсия + 1;
		КонецЦикла;
		НаборЗаписей.Загрузить(Выгрузка);
		НаборЗаписей.Записать();
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ПроверитьОбновитьДействующиеПараметрыОграниченияДоступа.
Функция ИспользованиеВидовДоступаИзменено(ДействующиеПараметры)
	
	Если ДействующиеПараметры.ВнешниеПользователиВключены
	        <> Константы.ИспользоватьВнешнихПользователей.Получить()
	 Или ДействующиеПараметры.ОграничениеДоступаВключено
	        <> Константы.ОграничиватьДоступНаУровнеЗаписей.Получить() Тогда
		Возврат Истина;
	КонецЕсли;
	
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	ТекущиеИспользуемыеТипыЗначений = ИспользуемыеТипыЗначений(СвойстваВидовДоступа,,, Истина);
	
	ИспользуемыеТипыЗначений = ДействующиеПараметры.ИспользуемыеТипыЗначений.Получить();
	
	Если ТипЗнч(ИспользуемыеТипыЗначений) <> Тип("Структура")
	 Или Не ИспользуемыеТипыЗначений.Свойство("ХешСумма")
	 Или ИспользуемыеТипыЗначений.ХешСумма <> ТекущиеИспользуемыеТипыЗначений.ХешСумма Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Для процедур ОбновлениеДоступаНаУровнеЗаписей, ВыполнитьОбновлениеДоступа,
// ОтменитьОбновлениеДоступаНаУровнеЗаписей, ЗавершитьОбновлениеДоступа.
//
Процедура ЗавершитьПотокиОбновленияДоступа(ОтменитьОбновление = Ложь)
	
	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаТекущиеЗадания);
	
	Если ОтменитьОбновление Тогда
		НаборЗаписей.Добавить().ИдентификаторПотока = ИдентификаторОтменыОбновленияДоступаНаУровнеЗаписей();
	Иначе
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА КАК ЗначениеИстина
		|ИЗ
		|	РегистрСведений.ОбновлениеКлючейДоступаТекущиеЗадания КАК ОбновлениеКлючейДоступаТекущиеЗадания";
		
		Если Запрос.Выполнить().Пустой() Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	НаборЗаписей.Записать();
	
КонецПроцедуры

// Для процедур ОбработатьВыполненныеЗадания, ЗавершитьПотокиОбновленияДоступа.
Функция ИдентификаторОтменыОбновленияДоступаНаУровнеЗаписей()
	
	Возврат Новый УникальныйИдентификатор("06cc4b5f-a2f9-4622-bef0-df4870ab5dd5");
	
КонецФункции

// Для процедур ОбновлениеДоступаНаУровнеЗаписей, ВыполнитьОбновлениеДоступа,
// ОтменитьОбновлениеДоступаНаУровнеЗаписей, ЗавершитьОбновлениеДоступа.
//
Процедура ОтменитьФоновыеЗаданияПотоковОбновленияДоступа(СекундОжидания = 5)
	
	Отбор = Новый Структура;
	Отбор.Вставить("Состояние", СостояниеФоновогоЗадания.Активно);
	Отбор.Вставить("ИмяМетода", ИмяМетодаПотокаОбновленияДоступа());
	
	ФоновыеЗаданияПотоков = ФоновыеЗадания.ПолучитьФоновыеЗадания(Отбор);
	Если ФоновыеЗаданияПотоков.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ФоновыеЗаданияПотоков = ФоновыеЗадания.ОжидатьЗавершенияВыполнения(ФоновыеЗаданияПотоков, СекундОжидания);
	Для Каждого ФоновоеЗаданиеПотока Из ФоновыеЗаданияПотоков Цикл
		Если ФоновоеЗаданиеПотока.Состояние = СостояниеФоновогоЗадания.Активно Тогда
			ФоновоеЗаданиеПотока.Отменить();
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Обновляет ключи доступа к данным на основе записей в регистре сведений
// ОбновлениеКлючейДоступаКДанным и ключи доступа пользователей на основе записей
// регистра сведений ОбновлениеКлючейДоступаПользователей.
//
// Обновляется порция данных для каждой таблицы, начиная с самых свежих данных.
// Процедура должна вызываться повторно, пока обработка не будет завершена,
// при этом регламентное задание выключается.
//
// Процедура предназначена для работы в единственном экземпляре, то есть без параллельной
// работы (вызов из процедуры регламентного задания ОбновлениеДоступаНаУровнеЗаписей).
// Параллельность обеспечивает сама процедура путем запуска до двух фоновых заданий
// на каждый список, но не более значения константы КоличествоПотоковОбновленияДоступа.
//
Процедура ВыполнитьОбновлениеДоступа(Знач ОбновитьВсе, ОписаниеОсновногоСеанса,
				ОбновлениеОтменено, ТекстОшибкиЗавершения, СекундНеБолее, ДатаПолногоЗавершения)
	
	Если СекундНеБолее > 0 Тогда
		ОбновитьВсе = Ложь;
	КонецЕсли;
	
	Контекст = НовыйКонтекстОбновленияДоступа();
	Контекст.Вставить("ОписаниеОсновногоСеанса",  ОписаниеОсновногоСеанса);
	Контекст.Вставить("ТекущееФоновоеЗадание",    ОписаниеОсновногоСеанса.ФоновоеЗадание);
	Контекст.Вставить("Задания",                  ТаблицаЗаданийОбновления());
	Контекст.Вставить("ЗаданияДляЗапуска",        Новый Массив);
	Контекст.Вставить("ЗанятыеПотоки",            Новый Соответствие);
	Контекст.Вставить("СвободныеПотоки",          Новый Массив);
	Контекст.Вставить("ГраницаОбновленияЗаданий", '00010101');
	Контекст.Вставить("ОбщиеПараметрыОбновления", ОписаниеОбщихПараметровОбновления());
	Контекст.Вставить("ОбновлениеВЭтомСеансе",    Ложь);
	Контекст.Вставить("КоличествоПотоков",        0);
	Контекст.Вставить("ДатаПолногоЗавершения",    ДатаПолногоЗавершения);
	Контекст.Вставить("ТекстОшибкиЗавершения",    "");
	Контекст.Вставить("ЕстьОтложенныеЗадания",    Ложь);
	Контекст.Вставить("ЕстьЗапущенноеЗадание",    Истина);
	Контекст.Вставить("ОбработкаЗавершена",       Ложь);
	Контекст.Вставить("ОбновлениеОтменено",       Ложь);
	Контекст.Вставить("ТребуетсяПерезапускСеанса",Ложь);
	Контекст.Вставить("МаксимумПорцийИзИсходной", 0);
	Контекст.Вставить("КоличествоДополнительныхПорций", 0);
	Контекст.Вставить("ЕстьДлительноеЗаданиеПолученияПорцийЭлементовДанных", Ложь);
	Контекст.Вставить("МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных", 0);
	Контекст.Вставить("ИдентификаторыОтключенныхОбъектовМетаданных", Новый Соответствие);
	Контекст.Вставить("ИдентификаторСправочникаНаборыГруппДоступа",
		ОбщегоНазначения.ИдентификаторОбъектаМетаданных("Справочник.НаборыГруппДоступа"));
	ОписаниеОсновногоСеанса.Удалить("ФоновоеЗадание");
	
	Запрос = Новый Запрос;
	Запрос.Текст = ТекстЗапросаЗаданий();
	Запрос.УстановитьПараметр("МаксимальнаяДата", МаксимальнаяДата());
	Запрос.УстановитьПараметр("ПустойИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	ЗавершитьПотокиОбновленияДоступа();
	ОтменитьФоновыеЗаданияПотоковОбновленияДоступа(1);
	
	Задания = Контекст.Задания;
	ЗанятыеПотоки = Контекст.ЗанятыеПотоки;
	
	Если СекундНеБолее > 0 Тогда
		ОкончаниеВыполнения = ТекущаяДатаСеанса() + СекундНеБолее;
	Иначе
		ОкончаниеВыполнения = ТекущаяДатаСеанса()
			+ МаксимальноеКоличествоМинутВыполненияФоновогоЗаданияОбновленияДоступа() * 60;
	КонецЕсли;
	
	ЗаполнитьКоличествоПотоков(Контекст);
	Контекст.Вставить("Показатели", ПоказателиОбновленияУправляющегоПотока(Контекст));
	
	Пока Истина Цикл
		Если Не ОбновитьВсе И ТекущаяДатаСеанса() > ОкончаниеВыполнения
		 Или ЗначениеЗаполнено(Контекст.ТекстОшибкиЗавершения) Тогда
			Прервать;
		КонецЕсли;
		Контекст.ОбработкаЗавершена = Истина;
		
		ЗаполнитьКоличествоПотоков(Контекст);
		// @skip-check query-in-loop - Порционная обработка данных
		ОбработатьВыполненныеЗадания(Контекст);
		
		Если Контекст.ОбновлениеОтменено Или Контекст.ТребуетсяПерезапускСеанса Тогда
			Контекст.ОбработкаЗавершена = Ложь;
			Прервать;
		КонецЕсли;
		
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
		ДобавитьЗаданияОбновленияДоступа(РезультатыЗапроса, Контекст);
		
		Если Контекст.ТребуетсяПерезапускСеанса Тогда
			Контекст.ОбработкаЗавершена = Ложь;
			Прервать;
		ИначеЕсли Контекст.ОбработкаЗавершена Тогда
			ОбновитьСоставИспользуемыхВерсийПараметровШаблонов(Контекст);
			Если Не Контекст.ОбработкаЗавершена Тогда
				Продолжить;
			КонецЕсли;
			Прервать;
		КонецЕсли;
		Контекст.ДатаПолногоЗавершения = '00010101';
		
		ОкончаниеТекущегоЗапуска = ТекущаяДатаСеанса() + 5;
		Контекст.Вставить("ПервыйПроход", Истина);
		
		Пока Истина Цикл
			
			Если Не ОбновитьВсе И ТекущаяДатаСеанса() > ОкончаниеВыполнения
			 Или ТекущаяДатаСеанса() > ОкончаниеТекущегоЗапуска
			 Или ЗначениеЗаполнено(Контекст.ТекстОшибкиЗавершения)
			 Или Задания.Количество() = 0
			 Или Контекст.ОбновлениеОтменено
			 Или Контекст.ТребуетсяПерезапускСеанса Тогда
				Прервать;
			КонецЕсли;
			
			ПрерватьПроход = Ложь;
			МоментПрерыванияПрохода = ТекущаяУниверсальнаяДатаВМиллисекундах() + 1000;
			ЗаполнитьОбщиеПараметрыОбновления(Контекст);
			Контекст.ЕстьЗапущенноеЗадание = Ложь;
			ЗаданияДляЗапуска = Контекст.ЗаданияДляЗапуска;
			
			Для Каждого Задание Из ЗаданияДляЗапуска Цикл
				
				Пока ЗанятыеПотоки.Количество() >= Контекст.КоличествоПотоков Цикл
					// @skip-check query-in-loop - Порционная обработка данных
					ОбработатьВыполненныеЗадания(Контекст);
					
					Если Контекст.ОбновлениеОтменено Или Контекст.ТребуетсяПерезапускСеанса Тогда
						ПрерватьПроход = Истина;
						Прервать;
					КонецЕсли;
					
					Если ЗанятыеПотоки.Количество() >= Контекст.КоличествоПотоков Тогда
						Если Не ОбновитьВсе И ТекущаяДатаСеанса() > ОкончаниеВыполнения
						 Или ТекущаяДатаСеанса() > ОкончаниеТекущегоЗапуска Тогда
							ПрерватьПроход = Истина;
							Прервать;
						КонецЕсли;
						// @skip-check query-in-loop - Порционная обработка данных
						ПодождатьОсвобожденияПотока(Контекст, Истина);
						Если Не Контекст.ПервыйПроход
						   И ТекущаяУниверсальнаяДатаВМиллисекундах() > МоментПрерыванияПрохода Тогда
							ПрерватьПроход = Истина;
							Прервать;
						КонецЕсли;
					КонецЕсли;
				КонецЦикла;
				Если ПрерватьПроход Тогда
					Прервать;
				КонецЕсли;
				Если ЗанятыеПотоки.Количество() < Контекст.КоличествоПотоков Тогда
					// @skip-check query-in-loop - Порционная обработка данных
					ЗапуститьОбновлениеДоступаСписка(Задание, Контекст);
					Если ЗначениеЗаполнено(Контекст.ТекстОшибкиЗавершения)
					 Или Контекст.ОбновлениеОтменено
					 Или Контекст.ТребуетсяПерезапускСеанса Тогда
						Прервать;
					КонецЕсли;
				КонецЕсли;
			КонецЦикла;
			Если Не Контекст.ЕстьЗапущенноеЗадание
			   И (ЗанятыеПотоки.Количество() >= Контекст.КоличествоПотоков
			      Или Не Контекст.ЕстьОтложенныеЗадания) Тогда
				// @skip-check query-in-loop - Порционная обработка данных
				ПодождатьОсвобожденияПотока(Контекст, Истина);
			КонецЕсли;
			// @skip-check query-in-loop - Порционная обработка данных
			ОбработатьВыполненныеЗадания(Контекст);
			Контекст.ПервыйПроход = Ложь;
		КонецЦикла;
	КонецЦикла;
	
	Если Не Контекст.ОбработкаЗавершена
	   И Не ЗначениеЗаполнено(Контекст.ТекстОшибкиЗавершения)
	   И Не Контекст.ОбновлениеОтменено
	   И Не Контекст.ТребуетсяПерезапускСеанса Тогда
		
		ОбновитьСоставИспользуемыхВерсийПараметровШаблонов(Контекст);
	КонецЕсли;
	
	ЗавершитьОбновлениеДоступа(Контекст);
	
	ТекстОшибкиЗавершения = Контекст.ТекстОшибкиЗавершения;
	ОбновлениеОтменено    = Контекст.ОбновлениеОтменено;
	ДатаПолногоЗавершения = Контекст.ДатаПолногоЗавершения;
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура:
//   * ОписаниеОсновногоСеанса - см. ОписаниеОсновногоСеанса
//   * ТекущееФоновоеЗадание - ФоновоеЗадание
//   * Задания - см. ТаблицаЗаданийОбновления
//   * ЗаданияДляЗапуска - Массив из СтрокаТаблицыЗначений: см. ТаблицаЗаданийОбновления
//   * ЗанятыеПотоки - Соответствие из КлючИЗначение:
//      ** Ключ - УникальныйИдентификатор
//      ** Значение - см. НовыйПоток
//   * СвободныеПотоки - Массив из см. НовыйПоток
//   * ГраницаОбновленияЗаданий - Дата
//   * ОбщиеПараметрыОбновления - см. ОписаниеОбщихПараметровОбновления
//   * ОбновлениеВЭтомСеансе - Булево
//   * КоличествоПотоков - Число
//   * ДатаПолногоЗавершения - Дата
//   * ТекстОшибкиЗавершения - Строка
//   * ЕстьОтложенныеЗадания - Булево
//   * ЕстьЗапущенноеЗадание - Булево
//   * ОбработкаЗавершена - Булево
//   * ОбновлениеОтменено - Булево
//   * ТребуетсяПерезапускСеанса - Булево
//   * МаксимумПорцийИзИсходной - Число
//   * КоличествоДополнительныхПорций - Число
//   * ЕстьДлительноеЗаданиеПолученияПорцийЭлементовДанных - Булево
//   * МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных - Число
//   * ИдентификаторыОтключенныхОбъектовМетаданных - Соответствие
//   * ИдентификаторСправочникаНаборыГруппДоступа - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//
Функция НовыйКонтекстОбновленияДоступа()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступаНаУровнеЗаписей.
Процедура ЗапланироватьОбработкуУстаревшихЭлементов(ТекстОшибкиПланирования,
			ПоследнееПланированиеОбработкиУстаревшихЭлементов)
	
	Если Не ЗначениеЗаполнено(ПоследнееПланированиеОбработкиУстаревшихЭлементов) Тогда
		ПоследнееПланированиеОбработкиУстаревшихЭлементов = ТекущаяДатаСеанса();
	КонецЕсли;
	Попытка
		ГраницаОжидания = ТекущаяДатаСеанса() - КоличествоЧасовМеждуПланированиемОбработкиУстаревшихЭлементов() * 60 * 60;
		Если ПоследнееПланированиеОбработкиУстаревшихЭлементов < ГраницаОжидания Тогда
			ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
			ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов = Истина;
			ПараметрыПланирования.Описание = "ОбновлениеДоступаНаУровнеЗаписей";
			ЗапланироватьОбновлениеДоступа(, ПараметрыПланирования);
			ПоследнееПланированиеОбработкиУстаревшихЭлементов = ТекущаяДатаСеанса();
		КонецЕсли;
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстОшибкиПланирования = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось запланировать обработку устаревших элементов ограничения доступа по причине:
			           |%1'"),
			ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ЗаполнитьКоличествоПотоков и формы ОбновлениеДоступаНаУровнеЗаписей.
//
// Возвращаемое значение:
//  Булево
//
Функция ДоступнаБалансировкаНагрузкиНаДиск() Экспорт
	
	Возврат МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных() > 0
		И УправлениеДоступомСлужебныйПовтИсп.ДоступнаБалансировкаНагрузкиНаДиск();
	
КонецФункции

// Для процедуры ЗаполнитьКоличествоПотоков и формы ОбновлениеДоступаНаУровнеЗаписей.
//
// Возвращаемое значение:
//  Булево
//
Функция БалансировкаНагрузкиНаДиск() Экспорт
	
	ЗначениеКонстанты = Константы.ПоследнееОбновлениеДоступа.Получить();
	ПоследнееОбновлениеДоступа = ПоследнееОбновлениеДоступа(ЗначениеКонстанты);
	Возврат ПоследнееОбновлениеДоступа.БалансировкаНагрузкиНаДиск;
	
КонецФункции

// Для формы ОбновлениеДоступаНаУровнеЗаписей.
Процедура УстановитьБалансировкуНагрузкиНаДиск(Использование) Экспорт
	
	БлокировкаДанных = Новый БлокировкаДанных;
	БлокировкаДанных.Добавить("Константа.ПоследнееОбновлениеДоступа");
	
	ЕстьВнешняяТранзакция = ТранзакцияАктивна();
	НачатьТранзакцию();
	Попытка
		БлокировкаДанных.Заблокировать();
		ЗначениеКонстанты = Константы.ПоследнееОбновлениеДоступа.Получить();
		ПоследнееОбновлениеДоступа = ПоследнееОбновлениеДоступа(ЗначениеКонстанты);
		
		Если ПоследнееОбновлениеДоступа.БалансировкаНагрузкиНаДиск <> Использование Тогда
			ПоследнееОбновлениеДоступа.БалансировкаНагрузкиНаДиск = Использование;
			УстановитьПоследнееОбновлениеДоступа(ПоследнееОбновлениеДоступа, ЕстьВнешняяТранзакция);
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступа.
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * ЕстьТочечноеЗадание - Булево
//   * ЕстьНачальноеОбновлениеТочечногоЗадания - Дата
//   * ДатаПоследнегоЗапускаТочечногоЗадания - Дата
//   * ДатаДобавленияТочечногоЗадания - Дата
//   * УровеньЗависимости - Число
//   * ЗависимыеСписки - Массив
//                     - Строка
//   * ЕстьНачальноеОбновление - Булево
//   * ЕстьПерезапуск - Булево
//   * ДатаПоследнегоЗапускаОбщегоЗадания - Дата
//   * ЕстьДатаПоследнегоОбновленногоЭлемента - Булево
//   * ДатаПоследнегоОбновленногоЭлемента - Дата
//   * ДатаДобавленияОбщегоЗадания - Дата
//   * ИдентификаторСписка - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                         - СправочникСсылка.ИдентификаторыОбъектовРасширений
//   * ДляВнешнихПользователей - Булево
//   * ЭтоОбновлениеПрав - Булево
//   * Запускать - Булево
//   * ЗанятыеПотоки - Соответствие из КлючИЗначение:
//      ** Ключ - УникальныйИдентификатор
//      ** Значение - см. НовыйПоток
//   * НаборПорций - Массив из см. ПорцияИзНабора
//   * КоличествоПорцийДляОбработки - Число
//   * ИндексСледующейПорцииДляОбработки - Число
//   * Пропустить - Булево
//   * Удалить - Булево
//   * ПорядокВидаКлючаДанных - Число
//   * ЭтоОбработкаУстаревшихЭлементов - Булево
//   * ОбновитьУровеньЗависимости - Булево
// 
Функция ТаблицаЗаданийОбновления()
	
	ТипыИдентификаторов = Новый Массив;
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных"));
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений"));
	
	Задания = Новый ТаблицаЗначений;
	Задания.Колонки.Добавить("ЕстьТочечноеЗадание",                    Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ЕстьНачальноеОбновлениеТочечногоЗадания",Новый ОписаниеТипов("Дата"));
	Задания.Колонки.Добавить("ДатаПоследнегоЗапускаТочечногоЗадания",  Новый ОписаниеТипов("Дата"));
	Задания.Колонки.Добавить("ДатаДобавленияТочечногоЗадания",         Новый ОписаниеТипов("Дата"));
	Задания.Колонки.Добавить("УровеньЗависимости",                     Новый ОписаниеТипов("Число"));
	Задания.Колонки.Добавить("ЗависимыеСписки",                        Новый ОписаниеТипов("Массив,Строка"));
	Задания.Колонки.Добавить("ЕстьНачальноеОбновление",                Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ЕстьПерезапуск",                         Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ДатаПоследнегоЗапускаОбщегоЗадания",     Новый ОписаниеТипов("Дата"));
	Задания.Колонки.Добавить("ЕстьДатаПоследнегоОбновленногоЭлемента", Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ДатаПоследнегоОбновленногоЭлемента",     Новый ОписаниеТипов("Дата"));
	Задания.Колонки.Добавить("ДатаДобавленияОбщегоЗадания",            Новый ОписаниеТипов("Дата"));
	Задания.Колонки.Добавить("ИдентификаторСписка",                    Новый ОписаниеТипов(ТипыИдентификаторов));
	Задания.Колонки.Добавить("ДляВнешнихПользователей",                Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ЭтоОбновлениеПрав",                      Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("Запускать",                              Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ЗанятыеПотоки",                          Новый ОписаниеТипов("Соответствие"));
	Задания.Колонки.Добавить("НаборПорций",                            Новый ОписаниеТипов("Массив"));
	Задания.Колонки.Добавить("КоличествоПорцийДляОбработки",           Новый ОписаниеТипов("Число"));
	Задания.Колонки.Добавить("ИндексСледующейПорцииДляОбработки",      Новый ОписаниеТипов("Число"));
	Задания.Колонки.Добавить("Пропустить",                             Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("Удалить",                                Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ПорядокВидаКлючаДанных",                 Новый ОписаниеТипов("Число"));
	Задания.Колонки.Добавить("ЭтоОбработкаУстаревшихЭлементов",        Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ОбновитьУровеньЗависимости",             Новый ОписаниеТипов("Булево"));
	
	Задания.Индексы.Добавить(
		"ЕстьТочечноеЗадание,
		|ЕстьНачальноеОбновлениеТочечногоЗадания,
		|ДатаПоследнегоЗапускаТочечногоЗадания,
		|ДатаДобавленияТочечногоЗадания,
		|ЕстьНачальноеОбновление,
		|ДатаПоследнегоЗапускаОбщегоЗадания,
		|ЕстьДатаПоследнегоОбновленногоЭлемента,
		|ДатаПоследнегоОбновленногоЭлемента,
		|ДатаДобавленияОбщегоЗадания");
	
	Задания.Индексы.Добавить("ИдентификаторСписка, ДляВнешнихПользователей, ЭтоОбновлениеПрав");
	Задания.Индексы.Добавить("ИдентификаторСписка, ДляВнешнихПользователей, ЭтоОбновлениеПрав, ЭтоОбработкаУстаревшихЭлементов");
	Задания.Индексы.Добавить("Удалить, ЭтоОбработкаУстаревшихЭлементов");
	Задания.Индексы.Добавить("УровеньЗависимости");
	
	Возврат Задания;
	
КонецФункции

// Для процедуры ДобавитьЗаданияОбновленияДоступа.
Функция ТаблицаКлючейЗаданий()
	
	ТипыИдентификаторов = Новый Массив;
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных"));
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений"));
	
	Задания = Новый ТаблицаЗначений;
	Задания.Колонки.Добавить("ИдентификаторСписка",     Новый ОписаниеТипов(ТипыИдентификаторов));
	Задания.Колонки.Добавить("ДляВнешнихПользователей", Новый ОписаниеТипов("Булево"));
	Задания.Колонки.Добавить("ЭтоОбновлениеПрав",       Новый ОписаниеТипов("Булево"));
	
	Задания.Индексы.Добавить("ИдентификаторСписка, ДляВнешнихПользователей, ЭтоОбновлениеПрав");
	
	Возврат Задания;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступа.
//
// Возвращаемое значение:
//  Структура:
//   * ИдентификаторПотока - УникальныйИдентификатор
//   * ФоновоеЗадание - ФоновоеЗадание
//   * Задание - СтрокаТаблицыЗначений из см. ТаблицаЗаданийОбновления
//   * ОтменитьЗадание - Булево
//   * ПорцияИзНабора - см. ПорцияИзНабора
//   * ДатаОсвобождения - Дата
//
Функция НовыйПоток()
	
	Поток = Новый Структура;
	Поток.Вставить("ИдентификаторПотока");
	Поток.Вставить("ФоновоеЗадание");
	Поток.Вставить("Задание");
	Поток.Вставить("ОтменитьЗадание", Ложь);
	Поток.Вставить("ПорцияИзНабора");
	Поток.Вставить("ДатаОсвобождения", '00010101');
	
	Возврат Поток;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступа.
//
// Возвращаемое значение:
//   Структура:
//     * ЭтоОбновлениеПрав               - Булево
//     * ИдентификаторСписка             - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                                       - СправочникСсылка.ИдентификаторыОбъектовРасширений
//     * ДляВнешнихПользователей         - Булево
//     * ЭтоОбработкаУстаревшихЭлементов - Булево
//     * ДатаНачала                      - Дата
//     * ДатаОкончания                   - Дата
//     * НачальноеОбновление             - Булево
//     * ОбработкаЗавершена              - Булево
//     * МаксимумМиллисекундОбработки    - Число
//     * ИдентификаторСправочникаНаборыГруппДоступа - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//     * Кэш                             - см. НовыйКэшКонтекста
//     * МаксимумПорцийИзИсходной        - Число
//
Функция ОписаниеОбщихПараметровОбновления(Контекст = Неопределено)
	
	ОбщиеПараметрыОбновления = Новый Структура;
	ОбщиеПараметрыОбновления.Вставить("ЭтоОбновлениеПрав",            Ложь);
	ОбщиеПараметрыОбновления.Вставить("ИдентификаторСписка",          Неопределено);
	ОбщиеПараметрыОбновления.Вставить("ДляВнешнихПользователей",      Ложь);
	ОбщиеПараметрыОбновления.Вставить("ЭтоОбработкаУстаревшихЭлементов", Ложь);
	ОбщиеПараметрыОбновления.Вставить("ДатаНачала",                   '00010101');
	ОбщиеПараметрыОбновления.Вставить("ДатаОкончания",                '00010101');
	ОбщиеПараметрыОбновления.Вставить("НачальноеОбновление",          Ложь);
	ОбщиеПараметрыОбновления.Вставить("ОбработкаЗавершена",           Истина);
	ОбщиеПараметрыОбновления.Вставить("МаксимумМиллисекундОбработки", 1000);
	
	Если Контекст = Неопределено Тогда
		ОбщиеПараметрыОбновления.Вставить("ИдентификаторСправочникаНаборыГруппДоступа",
			ОбщегоНазначения.ИдентификаторОбъектаМетаданных("Справочник.НаборыГруппДоступа"));
	Иначе
		ОбщиеПараметрыОбновления.Вставить("ИдентификаторСправочникаНаборыГруппДоступа",
			Контекст.ИдентификаторСправочникаНаборыГруппДоступа);
		
		Если Контекст.ОбновлениеВЭтомСеансе Тогда
			ОбщиеПараметрыОбновления.Вставить("Кэш", Контекст.Кэш);
		КонецЕсли;
		ОбщиеПараметрыОбновления.Вставить("МаксимумПорцийИзИсходной", Контекст.МаксимумПорцийИзИсходной);
	КонецЕсли;
	
	Возврат ОбщиеПараметрыОбновления;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступа.
Функция ТекстЗапросаЗаданий()
	
	Возврат
	"ВЫБРАТЬ
	|	ЛОЖЬ КАК ЭтоОбновлениеПрав,
	|	Списки.Список КАК ИдентификаторСписка,
	|	Списки.ДляВнешнихПользователей КАК ДляВнешнихПользователей,
	|	МАКСИМУМ(Списки.ТочечноеЗадание) КАК ЕстьТочечноеЗадание,
	|	МАКСИМУМ(Списки.ТочечноеЗадание
	|			И Списки.ДатаПоследнегоОбновленногоЭлемента = &МаксимальнаяДата) КАК ЕстьНачальноеОбновлениеТочечногоЗадания,
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА Списки.ТочечноеЗадание
	|					И Списки.КлючУникальности <> &ПустойИдентификатор
	|				ТОГДА Списки.ДатаИзмененияЗаписиРегистра
	|			ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
	|		КОНЕЦ) КАК ДатаДобавленияТочечногоЗадания,
	|	МАКСИМУМ(Списки.РазмерЗадания = 2) КАК ЕстьОбработкаУстаревших,
	|	МАКСИМУМ(Списки.РазмерЗадания = 3) КАК ЕстьПолноеОбновление,
	|	МАКСИМУМ((НЕ Списки.ТочечноеЗадание
	|			ИЛИ Списки.КлючУникальности = &ПустойИдентификатор)
	|			И Списки.ДатаПоследнегоОбновленногоЭлемента = &МаксимальнаяДата) КАК ЕстьНачальноеОбновление,
	|	МАКСИМУМ(НЕ Списки.ТочечноеЗадание
	|			И Списки.КлючУникальности <> &ПустойИдентификатор) КАК ЕстьПерезапуск,
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА НЕ Списки.ТочечноеЗадание
	|					ИЛИ Списки.КлючУникальности = &ПустойИдентификатор
	|				ТОГДА Списки.ДатаПоследнегоОбновленногоЭлемента
	|			ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
	|		КОНЕЦ) КАК ДатаПоследнегоОбновленногоЭлемента,
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА НЕ Списки.ТочечноеЗадание
	|					И Списки.КлючУникальности <> &ПустойИдентификатор
	|				ТОГДА Списки.ДатаИзмененияЗаписиРегистра
	|			ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
	|		КОНЕЦ) КАК ДатаДобавленияОбщегоЗадания
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК Списки
	|
	|СГРУППИРОВАТЬ ПО
	|	Списки.Список,
	|	Списки.ДляВнешнихПользователей
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	ИСТИНА,
	|	Списки.Список,
	|	Списки.ДляВнешнихПользователей,
	|	МАКСИМУМ(Списки.ТочечноеЗадание),
	|	ЛОЖЬ,
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА Списки.ТочечноеЗадание
	|					И Списки.КлючУникальности <> &ПустойИдентификатор
	|				ТОГДА Списки.ДатаИзмененияЗаписиРегистра
	|			ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
	|		КОНЕЦ),
	|	МАКСИМУМ(Списки.РазмерЗадания = 2),
	|	МАКСИМУМ(Списки.РазмерЗадания = 3),
	|	ЛОЖЬ,
	|	МАКСИМУМ(НЕ Списки.ТочечноеЗадание
	|			И Списки.КлючУникальности <> &ПустойИдентификатор),
	|	ДАТАВРЕМЯ(1, 1, 1),
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА НЕ Списки.ТочечноеЗадание
	|					И Списки.КлючУникальности <> &ПустойИдентификатор
	|				ТОГДА Списки.ДатаИзмененияЗаписиРегистра
	|			ИНАЧЕ ДАТАВРЕМЯ(1, 1, 1)
	|		КОНЕЦ)
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаПользователей КАК Списки
	|
	|СГРУППИРОВАТЬ ПО
	|	Списки.Список,
	|	Списки.ДляВнешнихПользователей";
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступа.
Процедура ЗаполнитьКоличествоПотоков(Контекст)
	
	Если Контекст.ОбновлениеВЭтомСеансе Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		КоличествоПотоков = 1;
		Контекст.МаксимумПорцийИзИсходной = 5;
	Иначе
		КоличествоПотоков = Константы.КоличествоПотоковОбновленияДоступа.Получить();
		Контекст.МаксимумПорцийИзИсходной = 5 + (КоличествоПотоков - 1) * 2;
	КонецЕсли;
	
	Если КоличествоПотоков < 1 Тогда
		КоличествоПотоков = 1;
	КонецЕсли;
	
	Если Контекст.КоличествоПотоков = 0 Тогда
		Контекст.ОбновлениеВЭтомСеансе = КоличествоПотоков = 1;
	КонецЕсли;
	
	Контекст.КоличествоПотоков = КоличествоПотоков;
	
	Если Контекст.ОбновлениеВЭтомСеансе Тогда
		Возврат;
	КонецЕсли;
	
	Если ДоступнаБалансировкаНагрузкиНаДиск() И БалансировкаНагрузкиНаДиск() Тогда
		Контекст.МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных =
			МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных();
	Иначе
		Контекст.МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных = 0;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступа.
Процедура ДобавитьЗаданияОбновленияДоступа(РезультатыЗапроса, Контекст)
	
	Если Контекст.Свойство("Кэш") Тогда
		Кэш = Контекст.Кэш; // см. НовыйКэшКонтекста
	Иначе
		Кэш = НовыйКэшКонтекста();
		Контекст.Вставить("Кэш", Кэш);
		Контекст.Вставить("ИдентификаторыПоПолнымИменам", Новый Соответствие);
		Контекст.Вставить("ВерсияПараметровОграничения");
		Контекст.Вставить("ХешСуммаПараметровОграничения");
	КонецЕсли;
	Если Не Кэш.Свойство("ОбъектыМетаданныхПоИдентификаторам") Тогда
		Кэш.Вставить("ОбъектыМетаданныхПоИдентификаторам", Новый Соответствие);
	КонецЕсли;
	
	Попытка
		ПроверитьОбновитьДействующиеПараметрыОграниченияДоступа();
	Исключение
		Если СтандартныеПодсистемыСервер.ТребуетсяПерезапускСеанса() Тогда
			Контекст.ТребуетсяПерезапускСеанса = Истина;
			Если СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке()) Тогда
				Возврат;
			КонецЕсли;
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь);
	Задания = Контекст.Задания;
	ИдентификаторыСписков = Новый Массив;
	
	Если Контекст.ВерсияПараметровОграничения   <> ПараметрыСеанса.ПараметрыОграниченияДоступа.Версия
	 Или Контекст.ХешСуммаПараметровОграничения <> ПараметрыСеанса.ПараметрыОграниченияДоступа.ХешСумма Тогда
		
		Задания.ЗаполнитьЗначения(-1, "УровеньЗависимости");
		Задания.ЗаполнитьЗначения(Неопределено, "ЗависимыеСписки");
		Контекст.Вставить("ИдентификаторыПоПолнымИменам", Новый Соответствие);
		Контекст.Вставить("ВедущиеСпискиПоЗависимым", ТаблицаКлючейЗаданий());
		Контекст.ВедущиеСпискиПоЗависимым.Колонки.Добавить("ВедущиеСписки");
		
		ТекущиеИдентификаторыСписка = Задания.Скопировать(, "ИдентификаторСписка");
		ТекущиеИдентификаторыСписка.Свернуть("ИдентификаторСписка");
		ИдентификаторыСписков = ТекущиеИдентификаторыСписка.ВыгрузитьКолонку("ИдентификаторСписка");
		
		Контекст.ВерсияПараметровОграничения   = ПараметрыСеанса.ПараметрыОграниченияДоступа.Версия;
		Контекст.ХешСуммаПараметровОграничения = ПараметрыСеанса.ПараметрыОграниченияДоступа.ХешСумма;
	КонецЕсли;
	
	Задания.ЗаполнитьЗначения(Истина, "Удалить");
	Отбор = Новый Структура("ИдентификаторСписка, ДляВнешнихПользователей, ЭтоОбновлениеПрав");
	
	Выгрузка = РезультатыЗапроса[0].Выгрузить();
	
	Для Каждого Строка Из Выгрузка Цикл
		Если Контекст.ИдентификаторыОтключенныхОбъектовМетаданных.Получить(Строка.ИдентификаторСписка) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ЗаполнитьЗначенияСвойств(Отбор, Строка);
		Строки = Задания.НайтиСтроки(Отбор);
		Если Строки.Количество() = 0 Тогда
			Задание = Задания.Добавить();
			Задание.УровеньЗависимости = -1; // Не заполнен.
			Если Строка.ИдентификаторСписка <> Неопределено
			   И Кэш.ОбъектыМетаданныхПоИдентификаторам.Получить(Строка.ИдентификаторСписка) = Неопределено Тогда
				ИдентификаторыСписков.Добавить(Строка.ИдентификаторСписка);
			КонецЕсли;
			Если Строка.ЕстьОбработкаУстаревших И Не Строка.ЕстьТочечноеЗадание И Не Строка.ЕстьПолноеОбновление Тогда
				Задание.ПорядокВидаКлючаДанных = ПорядокВидаКлючаДанных("УстаревшиеЭлементы");
			КонецЕсли;
		Иначе
			Задание = Строки[0];
		КонецЕсли;
		ЗаполнитьЗначенияСвойств(Задание, Строка);
		Задание.Удалить = Ложь;
		ОбновитьСвойствоЭтоОбработкаУстаревшихЭлементов(Задание, Ложь);
		Задание.ЕстьДатаПоследнегоОбновленногоЭлемента
			= ЗначениеЗаполнено(Задание.ДатаПоследнегоОбновленногоЭлемента);
		
		Если Задание.ЕстьПерезапуск Тогда
			ОтменитьЗадание(Задание);
		КонецЕсли;
	КонецЦикла;
	
	Если ИдентификаторыСписков.Количество() > 0 Тогда
		ОбъектыМетаданныхПоИдентификаторам =
			ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(ИдентификаторыСписков, Ложь);
		Для Каждого КлючИЗначение Из ОбъектыМетаданныхПоИдентификаторам Цикл
			Кэш.ОбъектыМетаданныхПоИдентификаторам.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
			Если ТипЗнч(КлючИЗначение.Значение) = Тип("ОбъектМетаданных") Тогда
				Контекст.ИдентификаторыПоПолнымИменам.Вставить(КлючИЗначение.Значение.ПолноеИмя(), КлючИЗначение.Ключ);
			ИначеЕсли КлючИЗначение.Значение = Неопределено Тогда
				Контекст.ИдентификаторыОтключенныхОбъектовМетаданных.Вставить(КлючИЗначение.Ключ, Истина);
				Строки = Задания.НайтиСтроки(Новый Структура("ИдентификаторСписка", КлючИЗначение.Ключ));
				Для Каждого Строка Из Строки Цикл
					Задания.Удалить(Строка);
				КонецЦикла;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если Задания.Количество() > 0 Тогда
		Контекст.ОбработкаЗавершена = Ложь;
	КонецЕсли;
	
	ЗаполнитьУровниЗависимостиЗаданий(Контекст, ДействующиеПараметры.ВедущиеСписки);
	
КонецПроцедуры

// Для функции ДобавитьЗаданияОбновленияДоступа.
// 
// Возвращаемое значение:
//  Структура:
//   * ОбъектыМетаданныхПоИдентификаторам - Соответствие
//
Функция НовыйКэшКонтекста()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедур ДобавитьЗаданияОбновленияДоступа, ОбновитьСвойстваЗадания и
// функции ЗапуститьОбновлениеДоступаСписка.
//
Процедура ОбновитьСвойствоЭтоОбработкаУстаревшихЭлементов(Задание, ТребуетсяОбновитьУровеньЗависимостиПриИзменении = Истина)
	
	ЭтоОбработкаУстаревшихЭлементов = Не Задание.ЕстьТочечноеЗадание
		И Задание.ПорядокВидаКлючаДанных >= ПорядокВидаКлючаДанных("УстаревшиеЭлементы")
		И Задание.ПорядокВидаКлючаДанных < ПорядокВидаКлючаДанных("НетДанных");
	
	Если Задание.ЭтоОбработкаУстаревшихЭлементов <> ЭтоОбработкаУстаревшихЭлементов Тогда
		Задание.ЭтоОбработкаУстаревшихЭлементов = ЭтоОбработкаУстаревшихЭлементов;
		Задание.ОбновитьУровеньЗависимости = ТребуетсяОбновитьУровеньЗависимостиПриИзменении;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьОбщиеПараметрыОбновления.
Функция ЭтоОбновлениеСоставаНаборовГруппДоступа(Задание, Контекст)
	
	Возврат Задание.ИдентификаторСписка = Контекст.ИдентификаторСправочникаНаборыГруппДоступа
	      И Задание.ПорядокВидаКлючаДанных <= ПорядокВидаКлючаДанных("НаборыГруппРазрешенныеПользователям");
	
КонецФункции

// Для процедуры ДобавитьЗаданияОбновленияДоступа.
Процедура ЗаполнитьУровниЗависимостиЗаданий(Контекст, СвойстваВедущихСписков)
	
	Задания = Контекст.Задания;
	ОбъектыМетаданныхПоИдентификаторам = Контекст.Кэш.ОбъектыМетаданныхПоИдентификаторам;
	ИдентификаторыПоПолнымИменам       = Контекст.ИдентификаторыПоПолнымИменам;
	
	ЗаданияДляЗаполнения = Задания.НайтиСтроки(Новый Структура("УровеньЗависимости", -1));
	ВедущиеСпискиПоЗависимым = Контекст.ВедущиеСпискиПоЗависимым;
	ОтборЗадания = Новый Структура("ИдентификаторСписка, ДляВнешнихПользователей, ЭтоОбновлениеПрав");
	
	Для Каждого Задание Из ЗаданияДляЗаполнения Цикл
		УстановитьУровеньЗависимостиПоВедущим(Задание, Задания, ВедущиеСпискиПоЗависимым);
		
		ОбъектМетаданных = ОбъектыМетаданныхПоИдентификаторам.Получить(Задание.ИдентификаторСписка);
		Если ТипЗнч(ОбъектМетаданных) <> Тип("ОбъектМетаданных") Тогда
			Продолжить;
		КонецЕсли;
		ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
		СвойстваВедущегоСписка = СвойстваВедущихСписков.Получить(ПолноеИмя);
		Если СвойстваВедущегоСписка = Неопределено
		 Или СвойстваВедущегоСписка.ПоКлючамДоступа = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Если Задание.ДляВнешнихПользователей Тогда
			ЗависимыеСписки = СвойстваВедущегоСписка.ПоКлючамДоступа.ДляВнешнихПользователей;
		Иначе
			ЗависимыеСписки = СвойстваВедущегоСписка.ПоКлючамДоступа.ДляПользователей;
		КонецЕсли;
		Если ЗависимыеСписки = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ЗаполнитьЗначенияСвойств(ОтборЗадания, Задание);
		Для Каждого ЗависимыйСписок Из ЗависимыеСписки Цикл
			ИдентификаторЗависимого = ИдентификаторыПоПолнымИменам.Получить(ЗависимыйСписок);
			Если Задание.ЗависимыеСписки = Неопределено Тогда
				Задание.ЗависимыеСписки = Новый Массив;
			КонецЕсли;
			ЗаданиеЗависимыеСписки = Задание.ЗависимыеСписки;
			ЗаданиеЗависимыеСписки.Добавить(ИдентификаторЗависимого);
			ОтборЗадания.ИдентификаторСписка = ИдентификаторЗависимого;
			Найденные = ВедущиеСпискиПоЗависимым.НайтиСтроки(ОтборЗадания);
			Если Найденные.Количество() = 0 Тогда
				ВедущиеСписки = Новый Соответствие;
				НоваяСтрока = ВедущиеСпискиПоЗависимым.Добавить();
				ЗаполнитьЗначенияСвойств(НоваяСтрока, ОтборЗадания);
				НоваяСтрока.ВедущиеСписки = ВедущиеСписки;
			Иначе
				ВедущиеСписки = Найденные[0].ВедущиеСписки;
			КонецЕсли;
			ВедущиеСписки.Вставить(Задание.ИдентификаторСписка, Истина);
			ЗависимыеЗадания = Задания.НайтиСтроки(ОтборЗадания);
			Если ЗависимыеЗадания.Количество() > 0 Тогда
				УстановитьУровеньЗависимостиПоВедущим(ЗависимыеЗадания[0], Задания, ВедущиеСпискиПоЗависимым);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ЗаполнитьУровниЗависимостиЗаданий, ОбновитьУровеньЗависимости.
Процедура УстановитьУровеньЗависимостиПоВедущим(Задание, Задания, ВедущиеСпискиПоЗависимым)
	
	Задание.УровеньЗависимости = 0;
	Если Задание.ЭтоОбработкаУстаревшихЭлементов Тогда
		Возврат;
	КонецЕсли;
	
	ОтборЗадания = Новый Структура("ИдентификаторСписка, ДляВнешнихПользователей, ЭтоОбновлениеПрав",
		Задание.ИдентификаторСписка, Задание.ДляВнешнихПользователей, Задание.ЭтоОбновлениеПрав);
	
	Найденные = ВедущиеСпискиПоЗависимым.НайтиСтроки(ОтборЗадания);
	Если Найденные.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ОтборЗадания.Вставить("ЭтоОбработкаУстаревшихЭлементов", Ложь);
	
	Для Каждого ОписаниеВедущегоСписка Из Найденные[0].ВедущиеСписки Цикл
		ОтборЗадания.ИдентификаторСписка = ОписаниеВедущегоСписка.Ключ;
		ВедущиеЗадания = Задания.НайтиСтроки(ОтборЗадания);
		Если ВедущиеЗадания.Количество() = 0 Тогда
			Продолжить;
		КонецЕсли;
		Если Задание.УровеньЗависимости < ВедущиеЗадания[0].УровеньЗависимости + 1 Тогда
			Задание.УровеньЗависимости = ВедущиеЗадания[0].УровеньЗависимости + 1;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ЗаполнитьОбщиеПараметрыОбновления.
Процедура ОбновитьУровеньЗависимости(СвойстваЗадания, Задания, ВедущиеСпискиПоЗависимым)
	
	Если Не ЗначениеЗаполнено(СвойстваЗадания.ЗависимыеСписки) Тогда
		Возврат;
	КонецЕсли;
	ОтборЗадания = Новый Структура("ИдентификаторСписка, ДляВнешнихПользователей, ЭтоОбновлениеПрав");
	ЗаполнитьЗначенияСвойств(ОтборЗадания, СвойстваЗадания);
	
	Для Каждого ЗависимыйСписок Из СвойстваЗадания.ЗависимыеСписки Цикл
		ОтборЗадания.ИдентификаторСписка = ЗависимыйСписок;
		ЗависимыеЗадания = Задания.НайтиСтроки(ОтборЗадания);
		Если ЗависимыеЗадания.Количество() = 0 Тогда
			Продолжить;
		КонецЕсли;
		УстановитьУровеньЗависимостиПоВедущим(ЗависимыеЗадания[0], Задания, ВедущиеСпискиПоЗависимым);
		ОбновитьУровеньЗависимости(ЗависимыеЗадания[0], Задания, ВедущиеСпискиПоЗависимым);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступа.
Процедура ЗаполнитьОбщиеПараметрыОбновления(Контекст)
	
	Задания = Контекст.Задания;
	Задания.Сортировать(
		"ЕстьТочечноеЗадание Убыв,
		|ЕстьНачальноеОбновлениеТочечногоЗадания Убыв,
		|ДатаПоследнегоЗапускаТочечногоЗадания Возр,
		|ДатаДобавленияТочечногоЗадания Убыв,
		|ЕстьНачальноеОбновление Убыв,
		|ДатаПоследнегоЗапускаОбщегоЗадания Возр,
		|ЕстьДатаПоследнегоОбновленногоЭлемента Возр,
		|ДатаПоследнегоОбновленногоЭлемента Убыв,
		|ДатаДобавленияОбщегоЗадания Убыв");
	
	ЗаданияДляУдаления = Задания.НайтиСтроки(Новый Структура("Удалить", Истина));
	Для Каждого Задание Из ЗаданияДляУдаления Цикл
		Если Не Задание.Удалить Или Задание.ЗанятыеПотоки.Количество() > 0 Тогда
			Продолжить;
		КонецЕсли;
		СвойстваЗадания = Новый Структура("ИдентификаторСписка,
		|ДляВнешнихПользователей, ЭтоОбновлениеПрав, ЗависимыеСписки");
		ЗаполнитьЗначенияСвойств(СвойстваЗадания, Задание);
		Задания.Удалить(Задание);
		ОбновитьУровеньЗависимости(СвойстваЗадания, Задания, Контекст.ВедущиеСпискиПоЗависимым);
	КонецЦикла;
	ЗаданияДляОбновленияУровня = Задания.НайтиСтроки(Новый Структура("ОбновитьУровеньЗависимости", Истина));
	Для Каждого Задание Из ЗаданияДляОбновленияУровня Цикл
		УстановитьУровеньЗависимостиПоВедущим(Задание, Задания, Контекст.ВедущиеСпискиПоЗависимым);
		ОбновитьУровеньЗависимости(Задание, Задания, Контекст.ВедущиеСпискиПоЗависимым);
		Задание.ОбновитьУровеньЗависимости = Ложь;
	КонецЦикла;
	
	ОтборУровней = Новый Структура("Удалить, ЭтоОбработкаУстаревшихЭлементов", Ложь, Ложь);
	Уровни = Задания.Скопировать(ОтборУровней, "УровеньЗависимости");
	Уровни.Свернуть("УровеньЗависимости");
	Уровни.Сортировать("УровеньЗависимости");
	НаименьшийУровеньЗависимости = ?(Уровни.Количество() > 0, Уровни[0].УровеньЗависимости, 0);
	ЭтоОбработкаУстаревшихЭлементов = Уровни.Количество() = 0;
	ДатаПоследнегоЗапускаЗависимогоОбщегоЗадания = '00010101';
	Если Уровни.Количество() > 1 Или Не ЭтоОбработкаУстаревшихЭлементов Тогда
		Для Каждого Задание Из Задания Цикл
			Если Задание.Удалить
			 Или Задание.ЕстьТочечноеЗадание Тогда
				Продолжить;
			КонецЕсли;
			Если Задание.УровеньЗависимости > НаименьшийУровеньЗависимости
			   И ДатаПоследнегоЗапускаЗависимогоОбщегоЗадания < Задание.ДатаПоследнегоЗапускаОбщегоЗадания Тогда
				ДатаПоследнегоЗапускаЗависимогоОбщегоЗадания = Задание.ДатаПоследнегоЗапускаОбщегоЗадания;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	ЗапускаемоеЗависимоеОбщееЗадание =
		?(ДатаПоследнегоЗапускаЗависимогоОбщегоЗадания > ТекущаяДатаСеанса() - 10, Null, Неопределено);
	
	ОбщиеПараметрыОбновления = Контекст.ОбщиеПараметрыОбновления;
	ОбщиеПараметрыОбновления.ЭтоОбработкаУстаревшихЭлементов = ЭтоОбработкаУстаревшихЭлементов;
	ОбщиеПараметрыОбновления.НачальноеОбновление = Ложь;
	ПоследняяДата = '00010101';
	ЗаданиеСправочникаНаборыГруппДоступа = Неопределено;
	ГраницаОжиданияДляЗависимыхЗаданий = ТекущаяДатаСеанса() - 3;
	
	Для Каждого Задание Из Задания Цикл
		Если Задание.Удалить Тогда
			Продолжить;
		КонецЕсли;
		Если ЭтоОбновлениеСоставаНаборовГруппДоступа(Задание, Контекст) Тогда
			ЗаданиеСправочникаНаборыГруппДоступа = Задание;
		КонецЕсли;
		Задание.Пропустить = Ложь;
		Если Задание.ЕстьНачальноеОбновление Тогда
			ОбщиеПараметрыОбновления.НачальноеОбновление = Истина;
		Иначе
			Если Задание.УровеньЗависимости > НаименьшийУровеньЗависимости Тогда
				Если Задание.ЕстьТочечноеЗадание Тогда
					Если Задание.ДатаПоследнегоЗапускаТочечногоЗадания > ГраницаОжиданияДляЗависимыхЗаданий Тогда
						Задание.Пропустить = Истина;
						Продолжить;
					КонецЕсли;
				ИначеЕсли ЗапускаемоеЗависимоеОбщееЗадание <> Неопределено Тогда
					Задание.Пропустить = Истина;
					Продолжить;
				Иначе
					ЗапускаемоеЗависимоеОбщееЗадание = Задание;
				КонецЕсли;
			КонецЕсли;
			Если Не ЭтоОбработкаУстаревшихЭлементов И Задание.ЭтоОбработкаУстаревшихЭлементов Тогда
				Задание.Пропустить = Истина;
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		Если Задание.ЕстьПерезапуск Тогда
			ОбщиеПараметрыОбновления.НачальноеОбновление = Истина;
		КонецЕсли;
		Если Задание.ДатаПоследнегоОбновленногоЭлемента > ПоследняяДата Тогда
			ПоследняяДата = Задание.ДатаПоследнегоОбновленногоЭлемента;
		КонецЕсли;
	КонецЦикла;
	
	Если ОбщиеПараметрыОбновления.НачальноеОбновление Тогда
		// Начало обновления.
		ОбщиеПараметрыОбновления.ДатаНачала = НачалоДня(ТекущаяДатаСеанса()) - 7 * (60 * 60 * 24); // 7 Дней.
	Иначе
		// Продолжение обновления.
		МаксимальныйПериод = МаксимальныйПериодПолученияПорцийЗапросом();
		
		Если МаксимальныйПериод = "Неделя" Тогда
			ДатаНачала = НачалоНедели(ПоследняяДата);
			
		ИначеЕсли МаксимальныйПериод = "Месяц" Тогда
			ДатаНачала = НачалоМесяца(ПоследняяДата);
		Иначе
			ЭтотГод = Год(ТекущаяДатаСеанса()) - Год(ПоследняяДата) = 0;
			СмещениеМесяца = Месяц(ТекущаяДатаСеанса()) - Месяц(ПоследняяДата);
			
			Если ЭтотГод И СмещениеМесяца = 0 Тогда
				ДатаНачала = НачалоМесяца(ПоследняяДата);
				
			ИначеЕсли ЭтотГод И СмещениеМесяца < 3 Или МаксимальныйПериод = "Квартал" Тогда
				ДатаНачала = НачалоКвартала(ПоследняяДата);
			Иначе
				ДатаНачала = НачалоГода(ПоследняяДата);
			КонецЕсли;
		КонецЕсли;
		
		ОбщиеПараметрыОбновления.ДатаНачала = ДатаНачала;
	КонецЕсли;
	ОбщиеПараметрыОбновления.ДатаОкончания = ПоследняяДата;
	
	ЗаданияДляЗапуска = Новый Массив;
	Контекст.ЗаданияДляЗапуска = ЗаданияДляЗапуска;
	Контекст.ЕстьОтложенныеЗадания = Ложь;
	
	Для Каждого Задание Из Задания Цикл
		Если Задание.Удалить Или Задание.Пропустить Тогда
			Задание.Запускать = Ложь;
			Если Задание.Пропустить Тогда
				Контекст.ЕстьОтложенныеЗадания = Истина;
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		Если ЗаданиеСправочникаНаборыГруппДоступа <> Неопределено Тогда
			Если Задание.ЕстьТочечноеЗадание Или Задание = ЗаданиеСправочникаНаборыГруппДоступа Тогда
				Задание.Запускать = Истина;
			ИначеЕсли ОбщиеПараметрыОбновления.НачальноеОбновление Тогда
				Задание.Запускать = Задание.ЕстьНачальноеОбновление Или Задание.ЕстьПерезапуск;
			КонецЕсли;
			
		ИначеЕсли Задание.ЕстьТочечноеЗадание Или Задание.ЭтоОбновлениеПрав Тогда
			Задание.Запускать = Истина;
			
		ИначеЕсли ОбщиеПараметрыОбновления.НачальноеОбновление Тогда
			Задание.Запускать = Задание.ЕстьНачальноеОбновление Или Задание.ЕстьПерезапуск;
		Иначе
			Задание.Запускать = Не ПериодОбновленияДоПериодаДанных(ОбщиеПараметрыОбновления.ДатаНачала,
				Задание.ДатаПоследнегоОбновленногоЭлемента);
		КонецЕсли;
		
		Если Задание.Запускать Тогда
			ЗаданияДляЗапуска.Добавить(Задание);
		Иначе
			Контекст.ЕстьОтложенныеЗадания = Истина;
		КонецЕсли;
	КонецЦикла;
	
	Если Не ЗагружатьСвободныеПотокиСледующимиЗаданиямиПриДлительныхЗапросах() Тогда
		Контекст.ЕстьОтложенныеЗадания = Ложь;
	КонецЕсли;
	
	Если Не Контекст.ЕстьЗапущенноеЗадание
	   И Контекст.ЕстьОтложенныеЗадания
	   И Контекст.ЗанятыеПотоки.Количество() < Контекст.КоличествоПотоков Тогда
		
		Контекст.ЕстьОтложенныеЗадания = Ложь;
		КоличествоСвободныхПотоков = Контекст.КоличествоПотоков - Контекст.ЗанятыеПотоки.Количество();
		КоличествоДополнительныхЗаданий = 0;
		
		Для Каждого Задание Из Задания Цикл
			Если Задание.Удалить
			 Или Задание.Запускать
			 Или Не ЭтоОбработкаУстаревшихЭлементов
			   И Задание.ЭтоОбработкаУстаревшихЭлементов Тогда
				Продолжить;
			КонецЕсли;
			Если КоличествоДополнительныхЗаданий >= КоличествоСвободныхПотоков Тогда
				Контекст.ЕстьОтложенныеЗадания = Истина;
				Прервать;
			КонецЕсли;
			Задание.Запускать = Истина;
			ЗаданияДляЗапуска.Добавить(Задание);
			КоличествоДополнительныхЗаданий = КоличествоДополнительныхЗаданий + 1;
		КонецЦикла;
	КонецЕсли;
	
	КоличествоОсновныхЗаданий = ЗаданияДляЗапуска.Количество();
	Если КоличествоОсновныхЗаданий > 0 Тогда
		Контекст.КоличествоДополнительныхПорций = Цел(Контекст.КоличествоПотоков / КоличествоОсновныхЗаданий);
	Иначе
		Контекст.КоличествоДополнительныхПорций = 0;
	КонецЕсли;
	
	КоличествоЗаданий = Задания.Количество();
	Если КоличествоЗаданий = 0 Тогда
		ДостаточностьПотоков = 0;
	ИначеЕсли Контекст.КоличествоПотоков > КоличествоЗаданий Тогда
		ДостаточностьПотоков = 1;
	Иначе
		ДостаточностьПотоков = Контекст.КоличествоПотоков / КоличествоЗаданий;
	КонецЕсли;
	НагруженностьОтПотоков = Контекст.КоличествоПотоков * 0.025;
	Если НагруженностьОтПотоков > 1 Тогда
		НагруженностьОтПотоков = 1;
	КонецЕсли;
	ОбщиеПараметрыОбновления.МаксимумМиллисекундОбработки =
		Цел(МинимальноеКоличествоСекундОбработкиПорцииВОтдельномПотоке() * 1000
		* (1 + НагруженностьОтПотоков) * (1 + ДостаточностьПотоков));
	
КонецПроцедуры

// Для процедур ДобавитьЗаданияОбновленияДоступа, ЗапуститьОбновлениеДоступаСписка,
// ВыполнитьОбновлениеДоступаСписка.
//
Функция ПериодОбновленияДоПериодаДанных(ДатаНачала, ДатаПоследнегоОбновленногоЭлемента)
	
	// ДатаНачала - например, 01.01.2012, а дата окончания 31.12.2012,
	// при этом ДатаПоследнегоОбновленногоЭлемента, например, 03.01.2013.
	// В таком случае данные для обновления старее, чем период обновления.
	Возврат ЗначениеЗаполнено(ДатаПоследнегоОбновленногоЭлемента)
		  И ДатаНачала > ДатаПоследнегоОбновленногоЭлемента;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступа.
Процедура ОбновитьСоставИспользуемыхВерсийПараметровШаблонов(Контекст)
	
	Если Не Контекст.ОбработкаЗавершена
	   И ПараметрыСеанса.ПараметрыОграниченияДоступа.ДатаСоздания > ТекущаяДатаСеанса() - 5 * 60 Тогда
		
		Возврат;
	КонецЕсли;
	
	Попытка
		ПроверитьОбновитьДействующиеПараметрыОграниченияДоступа();
		ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь);
	Исключение
		Если СтандартныеПодсистемыСервер.ТребуетсяПерезапускСеанса() Тогда
			Контекст.ТребуетсяПерезапускСеанса = Истина;
			Если СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке()) Тогда
				Возврат;
			КонецЕсли;
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	
	ДополнительныйКонтекст = ДействующиеПараметры.ДополнительныйКонтекст;
	
	Если ИспользуетсяОдинВариантДоступа(ДополнительныйКонтекст.ДляПользователей)
	   И ИспользуетсяОдинВариантДоступа(ДополнительныйКонтекст.ДляВнешнихПользователей) Тогда
		
		Возврат;
	КонецЕсли;
	
	ОбщийКонтекст = ОбщийКонтекстРасчетаПараметровОграничения();
	ОбщийКонтекст.Вставить("СпискиСУстаревшимиВариантамиДоступа", Новый Массив);
	
	Попытка
		ДействующиеПараметрыОграниченияДоступа(Неопределено, ОбщийКонтекст, Истина);
	Исключение
		Если СтандартныеПодсистемыСервер.ТребуетсяПерезапускСеанса() Тогда
			Контекст.ТребуетсяПерезапускСеанса = Истина;
			Если СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке()) Тогда
				Возврат;
			КонецЕсли;
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	
	Если ЗначениеЗаполнено(ОбщийКонтекст.СпискиСУстаревшимиВариантамиДоступа) Тогда
		Контекст.ОбработкаЗавершена = Ложь;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступа.
Процедура ЗавершитьОбновлениеДоступа(Контекст)
	
	Если Не Контекст.ОбновлениеОтменено И Не Контекст.ТребуетсяПерезапускСеанса Тогда
		Если ЗначениеЗаполнено(Контекст.ТекстОшибкиЗавершения) Тогда
			ГраницаОжидания = ТекущаяДатаСеанса() + 3;
		Иначе
			ГраницаОжидания = ТекущаяДатаСеанса() + 15;
		КонецЕсли;
		Пока Контекст.ЗанятыеПотоки.Количество() > 0 Цикл
			// @skip-check query-in-loop - Порционная обработка данных
			ПодождатьОсвобожденияПотока(Контекст);
			// @skip-check query-in-loop - Порционная обработка данных
			ОбработатьВыполненныеЗадания(Контекст);
			Если ТекущаяДатаСеанса() > ГраницаОжидания Тогда
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ЗавершитьПотокиОбновленияДоступа();
	ОтменитьФоновыеЗаданияПотоковОбновленияДоступа();
	
	Попытка
		Если Контекст.ОбработкаЗавершена Тогда
			Контекст.Вставить("ОшибкаОжиданияБлокировкиДанных", Ложь);
			Попытка
				ОтключитьРегламентноеЗаданиеЕслиНетНовыхЗаданий(Контекст);
			Исключение
				Если Не Контекст.ОшибкаОжиданияБлокировкиДанных Тогда
					ВызватьИсключение;
				КонецЕсли;
			КонецПопытки;
			Если Не Константы.ПервоеОбновлениеДоступаЗавершилось.Получить() Тогда
				Константы.ПервоеОбновлениеДоступаЗавершилось.Установить(Истина);
			КонецЕсли;
		КонецЕсли;
	Исключение
		ЗарегистрироватьПоказателиОбновленияОсновногоПотока(Контекст);
		ВызватьИсключение;
	КонецПопытки;
	
	ЗарегистрироватьПоказателиОбновленияОсновногоПотока(Контекст);
	
КонецПроцедуры

// Для процедуры ОбновитьСоставИспользуемыхВерсийПараметровШаблонов.
Функция ИспользуетсяОдинВариантДоступа(ДополнительныйКонтекст)
	
	ИспользуетсяНесколькоВариантов = Ложь;
	
	Для Каждого КлючИЗначение Из ДополнительныйКонтекст.ОсновныеВариантыДоступа Цикл
		Если КлючИЗначение.Значение.Количество() > 1 Тогда
			ИспользуетсяНесколькоВариантов = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Не ИспользуетсяНесколькоВариантов;
	
КонецФункции

// Для процедуры ЗавершитьОбновлениеДоступа.
Процедура ОтключитьРегламентноеЗаданиеЕслиНетНовыхЗаданий(Контекст)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("НедоступныеСпискиДляОбновленияКлючейДоступаКДанным",       Новый Массив);
	Запрос.УстановитьПараметр("НедоступныеСпискиДляОбновленияКлючейДоступаПользователей", Новый Массив);
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ОбновлениеКлючейДоступаКДанным.Список КАК Список
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК ОбновлениеКлючейДоступаКДанным
	|ГДЕ
	|	НЕ ОбновлениеКлючейДоступаКДанным.Список В (&НедоступныеСпискиДляОбновленияКлючейДоступаКДанным)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ОбновлениеКлючейДоступаПользователей.Список КАК Список
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаПользователей КАК ОбновлениеКлючейДоступаПользователей
	|ГДЕ
	|	НЕ ОбновлениеКлючейДоступаПользователей.Список В (&НедоступныеСпискиДляОбновленияКлючейДоступаПользователей)";
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	
	Если Не РезультатыЗапроса[0].Пустой() Тогда
		УстановитьНедоступныеСписки(РезультатыЗапроса[0],
			Запрос.Параметры.НедоступныеСпискиДляОбновленияКлючейДоступаКДанным);
	КонецЕсли;
	
	Если Не РезультатыЗапроса[1].Пустой() Тогда
		УстановитьНедоступныеСписки(РезультатыЗапроса[1],
			Запрос.Параметры.НедоступныеСпискиДляОбновленияКлючейДоступаКДанным);
	КонецЕсли;
	
	Если Запрос.Параметры.НедоступныеСпискиДляОбновленияКлючейДоступаКДанным.Количество() > 0
	 Или Запрос.Параметры.НедоступныеСпискиДляОбновленияКлючейДоступаПользователей.Количество() > 0 Тогда
		
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
	КонецЕсли;
	
	Если РезультатыЗапроса[0].Пустой()
	   И РезультатыЗапроса[1].Пустой() Тогда
		
		Блокировка = Новый БлокировкаДанных;
		Блокировка.Добавить("РегистрСведений.ОбновлениеКлючейДоступаКДанным");
		Блокировка.Добавить("РегистрСведений.ОбновлениеКлючейДоступаПользователей");
		НачатьТранзакцию();
		Попытка
			Контекст.ОшибкаОжиданияБлокировкиДанных = Истина;
			Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
				РегламентныеЗаданияСервер.ЗаблокироватьРегламентноеЗадание(Новый УникальныйИдентификатор);
			КонецЕсли;
			Блокировка.Заблокировать();
			Контекст.ОшибкаОжиданияБлокировкиДанных = Ложь;
			
			РезультатыЗапроса = Запрос.ВыполнитьПакет();
			
			Если РезультатыЗапроса[0].Пустой()
			   И РезультатыЗапроса[1].Пустой() Тогда
				
				Если ЗначениеЗаполнено(Контекст.ДатаПолногоЗавершения) Тогда
					ГраницаОтключения = ТекущаяДатаСеанса()
						- КоличествоСекундПередОтключениемРегламентногоЗаданияПослеПолногоЗавершенияОбновления();
					Если Контекст.ДатаПолногоЗавершения < ГраницаОтключения Тогда
						УстановитьОбновлениеДоступа(Ложь);
					КонецЕсли;
				Иначе
					Контекст.ДатаПолногоЗавершения = ТекущаяДатаСеанса();
				КонецЕсли;
			Иначе
				Контекст.ОбработкаЗавершена = Ложь;
				Контекст.ДатаПолногоЗавершения = '00010101';
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			Контекст.ОбработкаЗавершена = Ложь;
			ВызватьИсключение;
		КонецПопытки;
	Иначе
		Контекст.ОбработкаЗавершена = Ложь;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОтключитьРегламентноеЗаданиеЕслиНетНовыхЗаданий.
//
// Параметры:
//  РезультатЗапроса  - РезультатЗапроса
//  НедоступныеСписки - Массив
//
Процедура УстановитьНедоступныеСписки(РезультатЗапроса, НедоступныеСписки)
	
	ОбъектыМетаданныхПоИдентификаторам = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(
		РезультатЗапроса.Выгрузить().ВыгрузитьКолонку("Список"), Ложь);
	
	Для Каждого КлючИЗначение Из ОбъектыМетаданныхПоИдентификаторам Цикл
		Если КлючИЗначение.Значение = Неопределено Тогда
			НедоступныеСписки.Добавить(КлючИЗначение.Ключ);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступа.
Функция ЗапуститьОбновлениеДоступаСписка(Задание, Контекст)
	
	Показатели = Контекст.Показатели;
	Если Показатели <> Неопределено Тогда
		Показатели.НачалоВыдачиЗадания = ТекущаяУниверсальнаяДатаВМиллисекундах();
	КонецЕсли;
	
	ОбработатьВыполненныеЗадания(Контекст, Задание.ЗанятыеПотоки);
	
	Если ЗначениеЗаполнено(Контекст.ТекстОшибкиЗавершения) Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Задание.ЗанятыеПотоки.Количество() > 0 И Контекст.ПервыйПроход Тогда
		Возврат Истина; // Уже запущено.
	КонецЕсли;
	
	Если ОбщееЗаданиеВыполняется(Задание)
	   И Не ЗапускатьОбновлениеПолученныхПорцийПриПолученииНовыхПорций() Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Задание.ЭтоОбработкаУстаревшихЭлементов
	   И Не Контекст.ОбщиеПараметрыОбновления.ЭтоОбработкаУстаревшихЭлементов Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ОбщиеПараметрыОбновления = ОписаниеОбщихПараметровОбновления(Контекст);
	ЗаполнитьЗначенияСвойств(ОбщиеПараметрыОбновления, Контекст.ОбщиеПараметрыОбновления);
	ОбщиеПараметрыОбновления.ЭтоОбновлениеПрав       = Задание.ЭтоОбновлениеПрав;
	ОбщиеПараметрыОбновления.ИдентификаторСписка     = Задание.ИдентификаторСписка;
	ОбщиеПараметрыОбновления.ДляВнешнихПользователей = Задание.ДляВнешнихПользователей;
	
	НаборПорций = Задание.НаборПорций;
	ПоследнийИндекс = НаборПорций.Количество() - 1;
	
	ИндексСледующейПорцииДляОбработки = Задание.ИндексСледующейПорцииДляОбработки;
	ПорцияДляОбработки = Неопределено;
	Для Индекс = ИндексСледующейПорцииДляОбработки По ПоследнийИндекс Цикл
		ПорцияЭлементов = НаборПорций.Получить(Индекс);
		Если Не ПорцияЭлементов.Обработана И Не ПорцияЭлементов.Обрабатывается Тогда
			ПорцияДляОбработки = ПорцияЭлементов;
			Задание.ИндексСледующейПорцииДляОбработки = Индекс;
			ИндексСледующейПорцииДляОбработки = Индекс + 1;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если ПорцияДляОбработки = Неопределено Тогда
		Задание.ИндексСледующейПорцииДляОбработки = ПоследнийИндекс + 1;
		ПредыдущаяПорция = Неопределено;
		
	ИначеЕсли Индекс > 0 Тогда
		ПредыдущаяПорция = НаборПорций.Получить(Индекс - 1);
	Иначе
		ПредыдущаяПорция = Неопределено;
	КонецЕсли;
	
	Если Не Задание.ЕстьТочечноеЗадание И ПорцияДляОбработки <> Неопределено Тогда
		Если ПериодОбновленияДоПериодаДанных(ОбщиеПараметрыОбновления.ДатаНачала,
		         ?(ПредыдущаяПорция = Неопределено, Задание.ДатаПоследнегоОбновленногоЭлемента,
		             ПредыдущаяПорция.ДатаПоследнегоЭлементаПорции)) Тогда
			Возврат Истина;
		КонецЕсли;
		ОбщиеПараметрыОбновления.Вставить("ПорцияИзНабора", ПорцияДляОбработки);
		ПорцияДляОбработки.Обрабатывается = Истина;
		Задание.ИндексСледующейПорцииДляОбработки = ИндексСледующейПорцииДляОбработки;
	Иначе
		ПоследняяПорция = ?(ПоследнийИндекс > -1, НаборПорций[ПоследнийИндекс], Неопределено);
		
		Если ПоследняяПорция <> Неопределено
		   И (ПоследняяПорция.ПоследнийЭлементПорции.ВидКлючаДанных
		       <> ПоследняяПорция.НовыйПоследнийЭлементПорции.ВидКлючаДанных
		      Или ПоследняяПорция.НовыйПоследнийЭлементПорции.КлючДанных = Null
		      Или Не ОбщиеПараметрыОбновления.ЭтоОбновлениеПрав
		        И ПериодОбновленияДоПериодаДанных(ОбщиеПараметрыОбновления.ДатаНачала,
		              ПоследняяПорция.ДатаПоследнегоЭлементаПорции)) Тогда
			
			ПолучитьПорции = 0;
		Иначе
			КоличествоПорцийДляОбработки = Задание.КоличествоПорцийДляОбработки;
			ПолучитьПорции = (Контекст.КоличествоДополнительныхПорций + 2) * 2;
			Если Задание.ЕстьТочечноеЗадание Тогда
				ПолучитьПорции = ПолучитьПорции - КоличествоПорцийДляОбработки;
				Если ПолучитьПорции - КоличествоПорцийДляОбработки < Цел(ПолучитьПорции / 3) Тогда
					ПолучитьПорции = Цел(ПолучитьПорции / 3);
				Иначе
					ПолучитьПорции = ПолучитьПорции - КоличествоПорцийДляОбработки;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		
		Если Не Задание.ЕстьТочечноеЗадание И ПолучитьПорции = 0 Тогда
			Возврат Истина;
		КонецЕсли;
		
		ОбщиеПараметрыОбновления.Вставить("ПолучитьПорции", ПолучитьПорции);
		Если ПоследняяПорция <> Неопределено Тогда
			ОбщиеПараметрыОбновления.Вставить("НовыйПоследнийЭлементПорции",
				ПоследняяПорция.НовыйПоследнийЭлементПорции);
		КонецЕсли;
	КонецЕсли;
	
	Если Не ОбщиеПараметрыОбновления.Свойство("ПорцияИзНабора") И ОбщееЗаданиеВыполняется(Задание) Тогда
		Возврат Истина;
	КонецЕсли;
	
	Если Контекст.ЕстьДлительноеЗаданиеПолученияПорцийЭлементовДанных
	   И Не Задание.ЭтоОбновлениеПрав
	   И Не Задание.ЭтоОбработкаУстаревшихЭлементов
	   И ОбщиеПараметрыОбновления.Свойство("ПолучитьПорции")
	   И ОбщиеПараметрыОбновления.ПолучитьПорции > 0 Тогда
		
		Если Не Задание.ЕстьТочечноеЗадание Тогда
			Возврат Ложь;
		КонецЕсли;
		ОбщиеПараметрыОбновления.ПолучитьПорции = 0;
	КонецЕсли;
	
	Если Задание.ЕстьТочечноеЗадание Тогда
		Задание.ЕстьТочечноеЗадание = Ложь;
		ОбновитьСвойствоЭтоОбработкаУстаревшихЭлементов(Задание);
		Задание.ДатаПоследнегоЗапускаТочечногоЗадания = ТекущаяДатаСеанса();
	Иначе
		Задание.ДатаПоследнегоЗапускаОбщегоЗадания = ТекущаяДатаСеанса();
	КонецЕсли;
	
	Если Контекст.ОбновлениеВЭтомСеансе Тогда
		ВыполнитьОбновлениеДоступаСпискаСПопыткамиПовтора(ОбщиеПараметрыОбновления, Контекст);
		ОбработатьРезультатЗадания(Контекст, ОбщиеПараметрыОбновления, Задание);
	Иначе
		Для Каждого СвободныйПоток Из Контекст.СвободныеПотоки Цикл
			Прервать;
		КонецЦикла;
		Если СвободныйПоток = Неопределено Тогда
			Если Контекст.ЗанятыеПотоки.Количество() >= Контекст.КоличествоПотоков Тогда
				Возврат Истина;
			КонецЕсли;
			СвободныйПоток = НовыйПоток();
			Параметры = Новый Массив;
			Параметры.Добавить(Контекст.ОписаниеОсновногоСеанса);
			СвободныйПоток.ФоновоеЗадание = ФоновыеЗадания.Выполнить(ИмяМетодаПотокаОбновленияДоступа(), Параметры,,
				НСтр("ru = 'Управление доступом: Поток обновления доступа на уровне записей'",
					ОбщегоНазначения.КодОсновногоЯзыка()));
			СвободныйПоток.ИдентификаторПотока = СвободныйПоток.ФоновоеЗадание.УникальныйИдентификатор;
			Контекст.СвободныеПотоки.Добавить(СвободныйПоток);
		КонецЕсли;
		
		ИдентификаторПотока = СвободныйПоток.ИдентификаторПотока;
		НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаТекущиеЗадания);
		НаборЗаписей.Отбор.ИдентификаторПотока.Установить(ИдентификаторПотока);
		ЗаписьНабора = НаборЗаписей.Добавить();
		ЗаписьНабора.ИдентификаторПотока = ИдентификаторПотока;
		ЗаписьНабора.ЭтоЗапуск = Истина;
		ЗаписьНабора.Параметры = Новый ХранилищеЗначения(ОбщиеПараметрыОбновления);
		ЗаписьНабора.ДатаИзмененияЗаписиРегистра = ТекущаяДатаСеанса();
		
		НаборЗаписей.Записать();
		
		СвободныйПоток.Задание = Задание;
		Если ОбщиеПараметрыОбновления.Свойство("ПорцияИзНабора") Тогда
			СвободныйПоток.ПорцияИзНабора = ОбщиеПараметрыОбновления.ПорцияИзНабора;
		КонецЕсли;
		Задание.ЗанятыеПотоки.Вставить(ИдентификаторПотока, СвободныйПоток);
		Контекст.ЗанятыеПотоки.Вставить(ИдентификаторПотока, СвободныйПоток);
		Контекст.СвободныеПотоки.Удалить(0);
		Если Показатели <> Неопределено Тогда
			СнятьПоказателиВыдачиЗаданий(Показатели);
		КонецЕсли;
		Контекст.ЕстьЗапущенноеЗадание = Истина;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Для функции ЗапуститьОбновлениеДоступаСписка и процедуры ОбработатьРезультатЗадания.
Функция ОбщееЗаданиеВыполняется(Задание)
	
	Для Каждого ОписаниеЗанятогоПотока Из Задание.ЗанятыеПотоки Цикл
		Если ОписаниеЗанятогоПотока.Значение.ПорцияИзНабора = Неопределено Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для функции ЗапуститьОбновлениеДоступаСписка и процедуры ОтменитьФоновыеЗаданияПотоковОбновленияДоступа.
Функция ИмяМетодаПотокаОбновленияДоступа()
	
	Возврат "УправлениеДоступомСлужебный.ВыполнитьОбновлениеДоступаСпискаВФоне";
	
КонецФункции

// Для процедур УстановитьОбновлениеДоступа, ЗапуститьОбновлениеДоступаНаУровнеЗаписей,
// ОтменитьОбновлениеДоступаНаУровнеЗаписей и функции ИсполнительОбновленияДоступа.
// 
Функция ИмяМетодаЗаданияОбновленияДоступа()
	
	Возврат Метаданные.РегламентныеЗадания.ОбновлениеДоступаНаУровнеЗаписей.ИмяМетода;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступа, ЗавершитьОбновлениеДоступа.
Процедура ПодождатьОсвобожденияПотока(Контекст, ЖдатьЗавершенияЗадания = Ложь)
	
	Если Контекст.ОбновлениеВЭтомСеансе Тогда
		Возврат;
	КонецЕсли;
	
	Показатели = Контекст.Показатели;
	Если Показатели <> Неопределено Тогда
		НачалоОжидания = ТекущаяУниверсальнаяДатаВМиллисекундах();
	КонецЕсли;
	
	Выполнять = Истина;
	
	Если ЖдатьЗавершенияЗадания Тогда
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ИдентификаторыПотоков", ИдентификаторыПотоков(Контекст.ЗанятыеПотоки));
		Запрос.УстановитьПараметр("КоличествоПотоков", Контекст.КоличествоПотоков);
		Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА КАК ЗначениеИстина
		|ИЗ
		|	РегистрСведений.ОбновлениеКлючейДоступаТекущиеЗадания КАК ТекущиеЗадания
		|ГДЕ
		|	ТекущиеЗадания.ИдентификаторПотока В(&ИдентификаторыПотоков)
		|
		|СГРУППИРОВАТЬ ПО
		|	ТекущиеЗадания.ИдентификаторПотока
		|
		|ИМЕЮЩИЕ
		|	(МИНИМУМ(ТекущиеЗадания.ЭтоЗапуск) = ЛОЖЬ
		|		ИЛИ КОЛИЧЕСТВО(ТекущиеЗадания.ЭтоЗапуск) < &КоличествоПотоков)";
		Если Не Запрос.Выполнить().Пустой() Тогда
			Выполнять = Ложь;
		КонецЕсли;
		ГраницаОжидания = ТекущаяДатаСеанса() + 5;
	КонецЕсли;
	
	Пока Выполнять Цикл
		Если Контекст.ТекущееФоновоеЗадание <> Неопределено Тогда
			ФоновоеЗадание = Контекст.ТекущееФоновоеЗадание;
		Иначе
			ФоновоеЗадание = Неопределено;
			Для Каждого ОписаниеПотока Из Контекст.ЗанятыеПотоки Цикл
				Поток = ОписаниеПотока.Значение;
				ОбновитьСвойстваФоновогоЗадания(Поток, Контекст);
				Если Поток.ФоновоеЗадание <> Неопределено
				   И Поток.ФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
					
					ФоновоеЗадание = Поток.ФоновоеЗадание;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Если ФоновоеЗадание = Неопределено Тогда
			Прервать;
		КонецЕсли;
		ФоновоеЗадание.ОжидатьЗавершенияВыполнения(0.025);
		Если Не ЖдатьЗавершенияЗадания
		 Или ТекущаяДатаСеанса() > ГраницаОжидания
		 Или Контекст.ЗанятыеПотоки.Количество() = 0 Тогда
			Прервать;
		КонецЕсли;
		// @skip-check query-in-loop - Порционная обработка данных
		Если Не Запрос.Выполнить().Пустой() Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если Показатели <> Неопределено Тогда
		Показатели.ВремяОжиданийСвободногоПотока = Показатели.ВремяОжиданийСвободногоПотока
			+ (ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоОжидания);
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ПодождатьОсвобожденияПотока, ОбработатьВыполненныеЗадания.
Функция ИдентификаторыПотоков(ЗанятыеПотоки)
	
	ИдентификаторыПотоков = Новый Массив;
	
	Для Каждого ОписаниеПотока Из ЗанятыеПотоки Цикл
		ИдентификаторыПотоков.Добавить(ОписаниеПотока.Ключ);
	КонецЦикла;
	
	Возврат ИдентификаторыПотоков;
	
КонецФункции

// Для процедур ПодождатьОсвобожденияПотока, УдалитьОстановленныеПотоки.
Процедура ОбновитьСвойстваФоновогоЗадания(Поток, Контекст)
	
	ФоновоеЗадание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Поток.ИдентификаторПотока);
	
	Если ФоновоеЗадание = Неопределено Тогда
		Если РегистрироватьПоказателиОбновленияДоступа() Тогда
			ЗарегистрироватьОшибкуОбновленияДоступа(ТекстОшибкиОбновленияСКонтекстом(
				НСтр("ru = 'Не удалось найти запущенное фоновое задание.'"), Поток.Задание, Истина), Контекст);
		КонецЕсли;
		Контекст.ОбработкаЗавершена = Ложь;
		Возврат;
	КонецЕсли;
	Поток.ФоновоеЗадание = ФоновоеЗадание;
	
КонецПроцедуры

// Для процедуры ЗавершитьОбновлениеДоступа.
Процедура ОтменитьФоновоеЗаданиеПотока(Поток, Контекст)
	
	ОбновитьСвойстваФоновогоЗадания(Поток, Контекст);
	ФоновоеЗадание = Поток.ФоновоеЗадание;
	
	Если ФоновоеЗадание = Неопределено
	 Или ФоновоеЗадание.Состояние <> СостояниеФоновогоЗадания.Активно Тогда
		
		Возврат;
	КонецЕсли;
	
	Попытка
		ФоновоеЗадание.Отменить();
	Исключение
		ПредставлениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось отменить фоновое задание потока по причине:
			           |%1'"), ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		ЗарегистрироватьОшибкуОбновленияДоступа(ТекстОшибкиОбновленияСКонтекстом(
			ПредставлениеОшибки, Поток.Задание, Истина), Контекст);
	КонецПопытки;
	
	Контекст.ОбработкаЗавершена = Ложь;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступа, ОбработатьРезультатЗадания.
Процедура ОбработатьВыполненныеЗадания(Контекст, ЗанятыеПотоки = Неопределено)
	
	Если Контекст.ОбновлениеВЭтомСеансе Тогда
		Если ОбновлениеДоступаОтменено() Тогда
			Контекст.ОбновлениеОтменено = Истина;
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Показатели = Контекст.Показатели;
	Если Показатели <> Неопределено Тогда
		НачалоОбработки = ТекущаяУниверсальнаяДатаВМиллисекундах();
	КонецЕсли;
	
	Если ЗанятыеПотоки = Неопределено Тогда
		ЗанятыеПотоки = Контекст.ЗанятыеПотоки;
	КонецЕсли;
	ОписаниеПотоков = Новый Соответствие(Новый ФиксированноеСоответствие(ЗанятыеПотоки));
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИдентификаторОтмены", ИдентификаторОтменыОбновленияДоступаНаУровнеЗаписей());
	Запрос.УстановитьПараметр("ИдентификаторыПотоков", ИдентификаторыПотоков(ОписаниеПотоков));
	Запрос.УстановитьПараметр("ГраницаОжиданияВыполнения", ТекущаяДатаСеанса()
		- МаксимальноеКоличествоСекундОжиданияВыполненияОдногоЗаданияВПотоке());
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ТекущиеЗадания.ИдентификаторПотока КАК ИдентификаторПотока,
	|	ТекущиеЗадания.Результат КАК Результат,
	|	ТекущиеЗадания.ЭтоЗапуск
	|			И &ГраницаОжиданияВыполнения > ТекущиеЗадания.ДатаИзмененияЗаписиРегистра
	|		ИЛИ ТекущиеЗадания.ИдентификаторПотока = &ИдентификаторОтмены КАК ПревышеноВремяВыполнения,
	|	ТекущиеЗадания.ДатаИзмененияЗаписиРегистра КАК ДатаИзмененияЗаписиРегистра
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаТекущиеЗадания КАК ТекущиеЗадания
	|ГДЕ
	|	(НЕ ТекущиеЗадания.ЭтоЗапуск
	|				И ТекущиеЗадания.ИдентификаторПотока В (&ИдентификаторыПотоков)
	|			ИЛИ ТекущиеЗадания.ЭтоЗапуск
	|				И &ГраницаОжиданияВыполнения > ТекущиеЗадания.ДатаИзмененияЗаписиРегистра
	|			ИЛИ ТекущиеЗадания.ИдентификаторПотока = &ИдентификаторОтмены)";
	
	ОписаниеРезультатов = Новый Соответствие;
	Выборка = Запрос.Выполнить().Выбрать();
	
	Пока Выборка.Следующий() Цикл
		Если Выборка.ПревышеноВремяВыполнения = Истина Тогда
			Если Выборка.ИдентификаторПотока = ИдентификаторОтменыОбновленияДоступаНаУровнеЗаписей() Тогда
				Контекст.ОбновлениеОтменено = Истина;
				Возврат;
			КонецЕсли;
			Поток = Контекст.ЗанятыеПотоки.Получить(Выборка.ИдентификаторПотока);
			Если Поток = Неопределено Тогда
				Для Каждого СвободныйПоток Из Контекст.СвободныеПотоки Цикл
					Если СвободныйПоток.ИдентификаторПотока = Выборка.ИдентификаторПотока Тогда
						Поток = СвободныйПоток;
						Прервать;
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			Если Поток = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Если Поток.Задание.ИдентификаторСписка = Контекст.ИдентификаторСправочникаНаборыГруппДоступа
			   И Выборка.ДатаИзмененияЗаписиРегистра > (Запрос.Параметры.ГраницаОжиданияВыполнения
			       - МаксимальноеКоличествоСекундОжиданияВыполненияОдногоЗаданияВПотоке()) Тогда
				Продолжить;
			КонецЕсли;
			
			ПредставлениеОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Превышено время выполнения задания в потоке (%1 сек).
				           |Фоновое задание потока отменено и перезапущено.'"),
				МаксимальноеКоличествоСекундОжиданияВыполненияОдногоЗаданияВПотоке());
			
			ЗарегистрироватьОшибкуОбновленияДоступа(ТекстОшибкиОбновленияСКонтекстом(
				ПредставлениеОшибки, Поток.Задание, Истина), Контекст);
			
			ОтменитьФоновоеЗаданиеПотока(Поток, Контекст);
			УдалитьПоток(Поток, Контекст);
			ОписаниеПотоков.Удалить(Поток.ИдентификаторПотока);
			
			Если Показатели <> Неопределено Тогда
				Показатели.КоличествоПотоковСПревышениемВремениВыполнения =
					Показатели.КоличествоПотоковСПревышениемВремениВыполнения + 1;
			КонецЕсли;
			
		ИначеЕсли ТипЗнч(Выборка.Результат) = Тип("ХранилищеЗначения") Тогда
			ОписаниеРезультата = Новый Структура;
			ОписаниеРезультата.Вставить("ДатаЗавершения", Выборка.ДатаИзмененияЗаписиРегистра);
			ОписаниеРезультата.Вставить("Результат",      Выборка.Результат.Получить());
			ОписаниеРезультатов.Вставить(Выборка.ИдентификаторПотока, ОписаниеРезультата);
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого ОписаниеПотока Из ОписаниеПотоков Цикл
		Поток = ОписаниеПотока.Значение;
		ОписаниеРезультата = ОписаниеРезультатов.Получить(Поток.ИдентификаторПотока);
		ОбработатьРезультатВыполненногоЗадания(Поток, ОписаниеРезультата, Контекст);
	КонецЦикла;
	
	КоличествоПотоков = Контекст.ЗанятыеПотоки.Количество() + Контекст.СвободныеПотоки.Количество();
	Если КоличествоПотоков > Контекст.КоличествоПотоков Тогда
		Индекс = Контекст.СвободныеПотоки.Количество() - 1;
		Пока Индекс >= 0 Цикл
			СвободныйПоток = Контекст.СвободныеПотоки.Получить(Индекс);
			УдалитьПоток(СвободныйПоток, Контекст);
			Индекс = Индекс - 1;
			КоличествоПотоков = КоличествоПотоков - 1;
			Если КоличествоПотоков <= Контекст.КоличествоПотоков Тогда
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Контекст.ЕстьДлительноеЗаданиеПолученияПорцийЭлементовДанных = Ложь;
	ЗадержкаЗапроса = Контекст.МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных;
	Если ЗадержкаЗапроса > 0 Тогда
		ТекущиеЗанятыеПотоки = Новый Соответствие(Новый ФиксированноеСоответствие(Контекст.ЗанятыеПотоки));
		Для Каждого ОписаниеПотока Из ТекущиеЗанятыеПотоки Цикл
			Поток = ОписаниеПотока.Значение; // см. НовыйПоток
			Если Поток.ПорцияИзНабора <> Неопределено
			 Или Поток.Задание.ЭтоОбновлениеПрав
			 Или Поток.Задание.ЭтоОбработкаУстаревшихЭлементов Тогда
				Продолжить;
			КонецЕсли;
			Если ТекущаяДатаСеанса() - Поток.Задание.ДатаПоследнегоЗапускаОбщегоЗадания < ЗадержкаЗапроса Тогда
				Продолжить;
			КонецЕсли;
			Если Не Контекст.ЕстьДлительноеЗаданиеПолученияПорцийЭлементовДанных Тогда
				Контекст.ЕстьДлительноеЗаданиеПолученияПорцийЭлементовДанных = Истина;
				ЗадержкаЗапроса = ЗадержкаЗапроса / 2;
				Продолжить;
			КонецЕсли;
			ОтменитьФоновоеЗаданиеПотока(Поток, Контекст);
			УдалитьПоток(Поток, Контекст);
			Контекст.ЗанятыеПотоки.Удалить(Поток.ИдентификаторПотока);
		КонецЦикла;
	КонецЕсли;
	
	Если ТекущаяДатаСеанса() > Контекст.ГраницаОбновленияЗаданий Тогда
		Контекст.ГраницаОбновленияЗаданий = ТекущаяДатаСеанса() + 2;
		УдалитьОстановленныеПотоки(Контекст, Контекст.ЗанятыеПотоки);
		УдалитьОстановленныеПотоки(Контекст, Контекст.СвободныеПотоки);
		УдалитьНеиспользуемыеСвободныеПотоки(Контекст);
	КонецЕсли;
	
	Если Показатели <> Неопределено Тогда
		Показатели.ВремяОбработкиРезультатовЗаданий = Показатели.ВремяОбработкиРезультатовЗаданий
			+ (ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоОбработки);
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ОбработатьВыполненныеЗадания, ВыполнитьОбновлениеДоступаСпискаСПопыткамиПовтора.
Функция ОбновлениеДоступаОтменено()
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИдентификаторПотока", ИдентификаторОтменыОбновленияДоступаНаУровнеЗаписей());
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаТекущиеЗадания КАК ТекущиеЗадания
	|ГДЕ
	|	ТекущиеЗадания.ИдентификаторПотока = &ИдентификаторПотока";
	
	Возврат Не Запрос.Выполнить().Пустой();
	
КонецФункции

// Для процедуры ОбработатьВыполненныеЗадания.
Процедура ОбработатьРезультатВыполненногоЗадания(Поток, ОписаниеРезультата, Контекст)
	
	Если Не Поток.ОтменитьЗадание
	   И Не Поток.Задание.Удалить Тогда
		
		Если ОписаниеРезультата = Неопределено Тогда
			Возврат;
		ИначеЕсли ТипЗнч(ОписаниеРезультата.Результат) <> Тип("Структура") Тогда
			Контекст.ОбработкаЗавершена = Ложь;
		Иначе
			Результат = ОписаниеРезультата.Результат;
			Если Результат.Свойство("ПорцияИзНабора") Тогда
				Результат.ПорцияИзНабора = Поток.ПорцияИзНабора;
			КонецЕсли;
			Показатели = Контекст.Показатели;
			Если Показатели <> Неопределено Тогда
				Показатели.НачалоОбработкиЗадания = ТекущаяУниверсальнаяДатаВМиллисекундах();
			КонецЕсли;
			ОбработатьРезультатЗадания(Контекст, Результат, Поток.Задание);
			Если Показатели <> Неопределено Тогда
				СнятьПоказателиОбработкиРезультатаЗадания(Показатели);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если ОписаниеРезультата = Неопределено Тогда
		ДатаЗавершения = Неопределено;
	Иначе
		ДатаЗавершения = ОписаниеРезультата.ДатаЗавершения;
	КонецЕсли;
	
	ОсвободитьПоток(Поток, Контекст, ДатаЗавершения);
	
КонецПроцедуры

// Для процедур ОбработатьРезультатВыполненногоЗадания, УдалитьПоток.
Процедура ОсвободитьПоток(Поток, Контекст, ДатаЗавершения = Неопределено)
	
	ПозицияВставки = 0;
	Если ДатаЗавершения = Неопределено Тогда
		Поток.ДатаОсвобождения = ТекущаяДатаСеанса();
	Иначе
		Поток.ДатаОсвобождения = ДатаЗавершения;
		Количество = Контекст.СвободныеПотоки.Количество();
		Пока ПозицияВставки < Количество Цикл
			ТекущийПоток = Контекст.СвободныеПотоки.Получить(ПозицияВставки);
			Если ТекущийПоток.ДатаОсвобождения <= ДатаЗавершения Тогда
				Прервать;
			КонецЕсли;
			ПозицияВставки = ПозицияВставки + 1;
		КонецЦикла;
	КонецЕсли;
	
	Контекст.СвободныеПотоки.Вставить(ПозицияВставки, Поток);
	Контекст.ЗанятыеПотоки.Удалить(Поток.ИдентификаторПотока);
	Поток.Задание.ЗанятыеПотоки.Удалить(Поток.ИдентификаторПотока);
	Если Поток.ПорцияИзНабора <> Неопределено Тогда
		СнятьПризнакОбрабатываетсяДляПорции(Поток, Поток.Задание);
		Поток.ПорцияИзНабора = Неопределено;
	КонецЕсли;
	Поток.Задание = Неопределено;
	Поток.ОтменитьЗадание = Ложь;
	
КонецПроцедуры

// Для процедур ЗапуститьОбновлениеДоступаСписка, ОбработатьРезультатВыполненногоЗадания, ОсвободитьПоток.
Процедура СнятьПризнакОбрабатываетсяДляПорции(ПотокИлиРезультат, Задание)
	
	ПорцияИзНабора = Неопределено;
	
	Если Не ПотокИлиРезультат.Свойство("ПорцияИзНабора", ПорцияИзНабора)
	 Или ПорцияИзНабора = Неопределено
	 Или Не ПорцияИзНабора.Обрабатывается Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ПорцияИзНабора.Обработана Тогда
		ИндексПорции = Задание.НаборПорций.Найти(ПорцияИзНабора);
		
		Если ИндексПорции = Неопределено Тогда
			Задание.ИндексСледующейПорцииДляОбработки = 0;
			
		ИначеЕсли Задание.ИндексСледующейПорцииДляОбработки > ИндексПорции Тогда
			Задание.ИндексСледующейПорцииДляОбработки = ИндексПорции;
		КонецЕсли;
	КонецЕсли;
	
	ПорцияИзНабора.Обрабатывается = Ложь;
	
КонецПроцедуры

// Для процедуры ОбработатьРезультатВыполненногоЗадания, ЗавершитьОбновлениеДоступа.
Процедура УдалитьПоток(Поток, Контекст)
	
	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаТекущиеЗадания);
	НаборЗаписей.Отбор.ИдентификаторПотока.Установить(Поток.ИдентификаторПотока);
	НаборЗаписей.Записать();
	
	Если Контекст.ЗанятыеПотоки.Получить(Поток.ИдентификаторПотока) <> Неопределено Тогда
		ОсвободитьПоток(Поток, Контекст);
	КонецЕсли;
	
	Индекс = Контекст.СвободныеПотоки.Найти(Поток);
	Если Индекс <> Неопределено Тогда
		Контекст.СвободныеПотоки.Удалить(Индекс);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбработатьРезультатВыполненногоЗадания, ЗавершитьОбновлениеДоступа.
Процедура УдалитьОстановленныеПотоки(Контекст, ОписаниеПотоков)
	
	Если ТипЗнч(ОписаниеПотоков) = Тип("Соответствие") Тогда
		ИсходноеОписаниеПотоков = Новый ФиксированноеСоответствие(ОписаниеПотоков);
	Иначе
		ИсходноеОписаниеПотоков = Новый ФиксированныйМассив(ОписаниеПотоков);
	КонецЕсли;
	
	Для Каждого ОписаниеПотока Из ИсходноеОписаниеПотоков Цикл
		Если ТипЗнч(ОписаниеПотока) = Тип("КлючИЗначение") Тогда
			Поток = ОписаниеПотока.Значение;
		Иначе
			Поток = ОписаниеПотока;
		КонецЕсли;
		ОбновитьСвойстваФоновогоЗадания(Поток, Контекст);
		ФоновоеЗадание = Поток.ФоновоеЗадание;
		
		Если ФоновоеЗадание <> Неопределено
		   И ФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
			Продолжить;
		КонецЕсли;
		Контекст.ОбработкаЗавершена = Ложь;
		
		УдалитьПоток(Поток, Контекст);
		
		Если ФоновоеЗадание <> Неопределено
		   И ФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно Тогда
			
			ЗарегистрироватьОшибкуОбновленияДоступа(ТекстОшибкиОбновленияСКонтекстом(
				ФоновоеЗадание.ИнформацияОбОшибке, Поток.Задание, Истина), Контекст);
			
			Если Контекст.Показатели <> Неопределено Тогда
				Контекст.Показатели.КоличествоПотоковСНештатнымЗавершением =
					Контекст.Показатели.КоличествоПотоковСНештатнымЗавершением + 1;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбработатьВыполненныеЗадания.
Процедура УдалитьНеиспользуемыеСвободныеПотоки(Контекст)
	
	Количество = Контекст.СвободныеПотоки.Количество();
	Если Количество = 0 Тогда
		Возврат;
	КонецЕсли;
	
	СвободныеПотоки = Контекст.СвободныеПотоки;
	Индекс = Количество - 1;
	ТекущаяДатаСеанса = ТекущаяДатаСеанса();
	
	Пока Индекс >= 0 Цикл
		Поток = СвободныеПотоки.Получить(Индекс);
		Если ТекущаяДатаСеанса > Поток.ДатаОсвобождения + 15 Тогда
			УдалитьПоток(Поток, Контекст);
		КонецЕсли;
		Индекс = Индекс - 1;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ДобавитьЗаданияОбновленияДоступа и ОбработатьРезультатЗадания.
Процедура ОтменитьЗадание(Задание)
	
	Задание.НаборПорций = Новый Массив;
	Задание.КоличествоПорцийДляОбработки = 0;
	Задание.ИндексСледующейПорцииДляОбработки = 0;
	
	Для Каждого ОписаниеПотока Из Задание.ЗанятыеПотоки Цикл
		ОписаниеПотока.Значение.ОтменитьЗадание = Истина;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ЗапуститьОбновлениеДоступаСписка, ОбработатьРезультатВыполненногоЗадания.
Процедура ОбработатьРезультатЗадания(Контекст, Результат, Задание)
	
	Если Результат.Свойство("НетЗаданий") Или Результат.Свойство("ПерезапускОбновления") Тогда
		Если Результат.Свойство("ПерезапускОбновления") Тогда
			Задание.ЕстьПерезапуск = Истина;
			Если Результат.Свойство("ПерезапускОбновленияСНачала") Тогда
				Задание.ЕстьНачальноеОбновление = Истина;
			КонецЕсли;
		КонецЕсли;
		ОтменитьЗадание(Задание);
		Если Результат.Свойство("НетЗаданий") Тогда
			СнятьПризнакОбрабатываетсяДляПорции(Результат, Задание);
			ОбновитьСвойстваЗадания(Задание, Новый Структура("КлючДанных", Null));
			Если Результат.НетЗаданий = "ОбъектМетаданныхОтключен" Тогда
				Контекст.ИдентификаторыОтключенныхОбъектовМетаданных.Вставить(Задание.ИдентификаторСписка, Истина);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если Результат.Свойство("ТребуетсяПерезапускСеанса") Тогда
		Контекст.ТребуетсяПерезапускСеанса = Истина;
		Контекст.ОбработкаЗавершена = Ложь;
		СтандартныеПодсистемыСервер.УстановитьТребуетсяПерезапускСеанса(Результат.ТребуетсяПерезапускСеанса);
		СнятьПризнакОбрабатываетсяДляПорции(Результат, Задание);
		Возврат;
	КонецЕсли;
	
	Если Результат.Свойство("ТекстОшибкиЗавершения") Тогда
		Если Не Результат.ОбработкаЗавершена Тогда
			Контекст.ОбработкаЗавершена = Ложь;
		КонецЕсли;
		Если ЗначениеЗаполнено(Результат.ТекстОшибкиЗавершения) Тогда
			СнятьПризнакОбрабатываетсяДляПорции(Результат, Задание);
			ДобавитьТекстОшибкиЗавершения(Контекст.ТекстОшибкиЗавершения, Результат.ТекстОшибкиЗавершения);
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Если Результат.Свойство("НетЗаданий") Тогда
		Возврат;
	КонецЕсли;
	
	Если Результат.Свойство("НачальноеОбновлениеЗавершено") Тогда
		Задание.ЕстьНачальноеОбновление = Ложь;
		Задание.ЕстьПерезапуск = Ложь;
	КонецЕсли;
	
	Если Результат.Свойство("ПорцияИзНабора") Тогда
		ИсходнаяПорцияИзНабора = Результат.ПорцияИзНабора;
		Если Результат.Свойство("НаборПорций") Тогда
			Индекс = Задание.НаборПорций.Найти(ИсходнаяПорцияИзНабора);
			Если Задание.ИндексСледующейПорцииДляОбработки > Индекс Тогда
				Задание.ИндексСледующейПорцииДляОбработки = Индекс + 1;
			КонецЕсли;
			Для Каждого НоваяПорцияИзИсходной Из Результат.НаборПорций Цикл
				Индекс = Индекс + 1;
				Задание.НаборПорций.Вставить(Индекс, НоваяПорцияИзИсходной);
			КонецЦикла;
			Задание.КоличествоПорцийДляОбработки = Задание.КоличествоПорцийДляОбработки
				+ Результат.НаборПорций.Количество();
			ИсходнаяПорцияИзНабора.ПоследнийЭлементПорции      = Результат.НовыйПоследнийОбновленныйЭлемент;
			ИсходнаяПорцияИзНабора.НовыйПоследнийЭлементПорции = Результат.НовыйПоследнийОбновленныйЭлемент;
		КонецЕсли;
		ИсходнаяПорцияИзНабора.Обработана = Истина;
		СнятьПризнакОбрабатываетсяДляПорции(Результат, Задание);
		
	ИначеЕсли Результат.Свойство("НаборПорций") Тогда
		Для Каждого НоваяПорция Из Результат.НаборПорций Цикл
			Задание.НаборПорций.Добавить(НоваяПорция);
		КонецЦикла;
		Задание.КоличествоПорцийДляОбработки = Задание.КоличествоПорцийДляОбработки
			+ Результат.НаборПорций.Количество();
		
	ИначеЕсли Результат.Свойство("НовыйПоследнийОбновленныйЭлемент") Тогда
		ОбновитьСвойстваЗадания(Задание, Результат.НовыйПоследнийОбновленныйЭлемент);
	КонецЕсли;
	
	Если Результат.Свойство("ПорцияИзНабора") И ОбщееЗаданиеВыполняется(Задание) Тогда
		Возврат;
	КонецЕсли;
	
	Зафиксировать = Ложь;
	Пока Задание.НаборПорций.Количество() > 0 Цикл
		ПорцияИзНабора = Задание.НаборПорций[0];
		Если Не ПорцияИзНабора.Обработана Тогда
			Прервать;
		КонецЕсли;
		Зафиксировать = Истина;
		НовыйПоследнийОбновленныйЭлемент = ПорцияИзНабора.НовыйПоследнийЭлементПорции;
		Задание.НаборПорций.Удалить(0);
		Если Задание.ИндексСледующейПорцииДляОбработки > 0 Тогда
			Задание.ИндексСледующейПорцииДляОбработки = Задание.ИндексСледующейПорцииДляОбработки - 1;
		КонецЕсли;
	КонецЦикла;
	
	Если Зафиксировать Тогда
		ЗаписатьПоследнийОбновленныйЭлемент(Результат, НовыйПоследнийОбновленныйЭлемент);
		ОбновитьСвойстваЗадания(Задание, НовыйПоследнийОбновленныйЭлемент);
		Если Не Результат.ОбработкаЗавершена Тогда
			Контекст.ОбработкаЗавершена = Ложь;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбработатьРезультатЗадания.
Процедура ОбновитьСвойстваЗадания(Задание, НовыйПоследнийОбновленныйЭлемент)
	
	Если НовыйПоследнийОбновленныйЭлемент.КлючДанных = Null Тогда
		Задание.Удалить = Истина;
	Иначе
		Если НовыйПоследнийОбновленныйЭлемент.Свойство("Дата") Тогда
			Задание.ДатаПоследнегоОбновленногоЭлемента = НовыйПоследнийОбновленныйЭлемент.Дата;
		Иначе
			Задание.ДатаПоследнегоОбновленногоЭлемента = '00010101';
		КонецЕсли;
		Задание.ЕстьДатаПоследнегоОбновленногоЭлемента
			= ЗначениеЗаполнено(Задание.ДатаПоследнегоОбновленногоЭлемента);
		
		Задание.ПорядокВидаКлючаДанных = НовыйПоследнийОбновленныйЭлемент.ПорядокВидаКлючаДанных;
		ОбновитьСвойствоЭтоОбработкаУстаревшихЭлементов(Задание);
	КонецЕсли;
	
КонецПроцедуры

// Для функции ЗапуститьОбновлениеДоступаСписка.
Процедура ВыполнитьОбновлениеДоступаСпискаВФоне(ОписаниеРодительскогоСеанса) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	Контекст = Новый Структура;
	Контекст.Вставить("Показатели", ПоказателиОбновленияИсполняющегоПотока());
	Контекст.Вставить("ОписаниеРодительскогоСеанса", ОписаниеРодительскогоСеанса);
	
	Если Не ИсполняющийПотокЗапущен(Контекст) Тогда
		Возврат;
	КонецЕсли;
	Кэш = Новый Структура;
	
	Пока Истина Цикл
		Запрос = Контекст.Запрос; // Запрос
		Выборка = Запрос.Выполнить().Выбрать();
		Если Не Выборка.Следующий() Тогда
			Прервать;
		КонецЕсли;
		Если ТипЗнч(Выборка.Параметры) <> Тип("ХранилищеЗначения") Тогда
			Если ПродолжитьОжиданиеНовогоЗадания(Контекст) Тогда
				Продолжить;
			Иначе
				Прервать;
			КонецЕсли;
		КонецЕсли;
		Результат = ОписаниеОбщихПараметровОбновления();
		Результат.Вставить("ОбработкаЗавершена", Ложь);
		Результат.Вставить("ТекстОшибкиЗавершения", "");
		Попытка
			ОбщиеПараметрыОбновления = Выборка.Параметры.Получить(); // см. ОписаниеОбщихПараметровОбновления
			ЗаполнитьЗначенияСвойств(Результат, ОбщиеПараметрыОбновления);
			ОбщиеПараметрыОбновления.Вставить("Кэш", Кэш);
			Контекст.КоличествоСекундОжиданияДоПоискаНовогоЗадания =
				Цел(0.025 * Результат.МаксимумМиллисекундОбработки) / 1000;
			
			ВыполнитьОбновлениеДоступаСпискаСПопыткамиПовтора(ОбщиеПараметрыОбновления, Контекст);
			Результат.ОбработкаЗавершена = ОбщиеПараметрыОбновления.ОбработкаЗавершена;
			
			ВозвращаемыеСвойства = "НетЗаданий, ПерезапускОбновления, ПерезапускОбновленияСНачала,
				|НаборПорций, НовыйПоследнийОбновленныйЭлемент, НачальноеОбновлениеЗавершено,
				|ТекстОшибкиЗавершения, ТребуетсяПерезапускСеанса";
			Для Каждого КлючИЗначение Из Новый Структура(ВозвращаемыеСвойства) Цикл
				Если ОбщиеПараметрыОбновления.Свойство(КлючИЗначение.Ключ) Тогда
					Результат.Вставить(КлючИЗначение.Ключ, ОбщиеПараметрыОбновления[КлючИЗначение.Ключ]);
				КонецЕсли;
			КонецЦикла;
			Если ОбщиеПараметрыОбновления.Свойство("ПорцияИзНабора") Тогда
				Результат.Вставить("ПорцияИзНабора");
			КонецЕсли;
		Исключение
			ДобавитьТекстОшибкиЗавершения(Результат.ТекстОшибкиЗавершения, ТекстОшибкиОбновленияСКонтекстом(
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()), Результат));
		КонецПопытки;
		ЗаписатьРезультатОбновленияДоступаСпискаВФоне(Результат, Выборка.Параметры, Контекст);
	КонецЦикла;
	
	Если РегистрироватьПоказателиОбновленияДоступа() Тогда
		ЗарегистрироватьПоказателиОбновленияИсполняющегоПотока(Контекст);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Функция ИсполняющийПотокЗапущен(Контекст)
	
	ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
	Контекст.Вставить("ТекущийСеанс", ТекущийСеанс);
	
	Если ТекущийСеанс.ИмяПриложения <> "BackgroundJob" Тогда
		ТекстОшибки = НСтр("ru = 'Порция обновления доступа может обрабатываться только в фоновом задании.'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Контекст.Вставить("ТекущееФоновоеЗадание", ТекущийСеанс.ПолучитьФоновоеЗадание());
	ИдентификаторПотока = Контекст.ТекущееФоновоеЗадание.УникальныйИдентификатор;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ИдентификаторПотока", ИдентификаторПотока);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ВЫБОР
	|		КОГДА ТекущиеЗадания.ЭтоЗапуск
	|			ТОГДА ТекущиеЗадания.Параметры
	|		ИНАЧЕ НЕОПРЕДЕЛЕНО
	|	КОНЕЦ КАК Параметры
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаТекущиеЗадания КАК ТекущиеЗадания
	|ГДЕ
	|	ТекущиеЗадания.ИдентификаторПотока = &ИдентификаторПотока";
	Контекст.Вставить("Запрос", Запрос);
	
	Блокировка = Новый("БлокировкаДанных");
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ОбновлениеКлючейДоступаТекущиеЗадания");
	ЭлементБлокировки.УстановитьЗначение("ИдентификаторПотока", ИдентификаторПотока);
	Контекст.Вставить("Блокировка", Блокировка);
	
	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаТекущиеЗадания);
	НаборЗаписей.Отбор.ИдентификаторПотока.Установить(ИдентификаторПотока);
	Контекст.Вставить("НаборЗаписей", НаборЗаписей);
	
	ЗаписьНабора = НаборЗаписей.Добавить();
	ЗаписьНабора.ИдентификаторПотока = ИдентификаторПотока;
	ЗаписьНабора.ЭтоЗапуск = Ложь;
	Контекст.Вставить("ЗаписьНабора", ЗаписьНабора);
	
	// Ожидание признака запуска.
	ГраницаОжидания = ТекущаяУниверсальнаяДатаВМиллисекундах() + 1000;
	Пока Истина Цикл
		Если Не Запрос.Выполнить().Пустой() Тогда
			Прервать;
		КонецЕсли;
		Если ТекущаяУниверсальнаяДатаВМиллисекундах() > ГраницаОжидания Тогда
			Возврат Ложь;
		КонецЕсли;
		Контекст.ТекущееФоновоеЗадание.ОжидатьЗавершенияВыполнения(0.025);
	КонецЦикла;
	
	Контекст.Вставить("ГраницаПроверкиРодительскогоСеанса", ТекущаяУниверсальнаяДатаВМиллисекундах() + 1000);
	Контекст.Вставить("КоличествоСекундОжиданияДоПоискаНовогоЗадания", 0.025);
	
	Возврат Истина;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Функция ПродолжитьОжиданиеНовогоЗадания(Контекст)
	
	Если ТекущаяУниверсальнаяДатаВМиллисекундах() > Контекст.ГраницаПроверкиРодительскогоСеанса Тогда
		Если ОбновлениеДоступаОтменено() Тогда
			Возврат Ложь;
		КонецЕсли;
		Контекст.ГраницаПроверкиРодительскогоСеанса = ТекущаяУниверсальнаяДатаВМиллисекундах() + 1000;
		Если Не СеансСуществует(Контекст.ОписаниеРодительскогоСеанса) Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Показатели = Контекст.Показатели;
	Если Показатели <> Неопределено Тогда
		Показатели.КоличествоОжиданийНовыхЗаданий = Показатели.КоличествоОжиданийНовыхЗаданий + 1;
	КонецЕсли;
	
	НачалоОжидания = ТекущаяУниверсальнаяДатаВМиллисекундах();
	
	Контекст.ТекущееФоновоеЗадание.ОжидатьЗавершенияВыполнения(
		Контекст.КоличествоСекундОжиданияДоПоискаНовогоЗадания);
	
	Если Показатели <> Неопределено Тогда
		Показатели.ВремяОжиданияНовыхЗаданий = Показатели.ВремяОжиданияНовыхЗаданий
			+ (ТекущаяУниверсальнаяДатаВМиллисекундах() - НачалоОжидания);
	КонецЕсли;
	Если Контекст.КоличествоСекундОжиданияДоПоискаНовогоЗадания < 1 Тогда
		Контекст.КоличествоСекундОжиданияДоПоискаНовогоЗадания =
			Контекст.КоличествоСекундОжиданияДоПоискаНовогоЗадания + 0.010;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Для функции ПродолжитьОжиданиеНовогоЗадания.
Функция СеансСуществует(ОписаниеСеанса)
	
	Если ЗначениеЗаполнено(ОписаниеСеанса.ИдентификаторФоновогоЗадания) Тогда
		ФоновоеЗадание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(
			ОписаниеСеанса.ИдентификаторФоновогоЗадания);
		
		Возврат ФоновоеЗадание <> Неопределено
		      И ФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.Активно;
	КонецЕсли;
	
	ОсновнойСеансНайден = Ложь;
	Сеансы = ПолучитьСеансыИнформационнойБазы();
	
	Для Каждого Сеанс Из Сеансы Цикл
		
		Если Сеанс.НачалоСеанса = ОписаниеСеанса.НачалоСеанса
		   И Сеанс.НомерСеанса  = ОписаниеСеанса.НомерСеанса Тогда
			
			ОсновнойСеансНайден = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат ОсновнойСеансНайден;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Процедура ЗаписатьРезультатОбновленияДоступаСпискаВФоне(Результат, ИсходныеПараметры, Контекст)
	
	НачатьТранзакцию();
	Попытка
		Контекст.Блокировка.Заблокировать();
		Запрос = Контекст.Запрос; // Запрос - 
		Выборка = Запрос.Выполнить().Выбрать();
		
		Если Выборка.Следующий()
		   И XMLСтрока(Выборка.Параметры) = XMLСтрока(ИсходныеПараметры) Тогда
			
			Контекст.ЗаписьНабора.Параметры = ИсходныеПараметры;
			Контекст.ЗаписьНабора.Результат = Новый ХранилищеЗначения(Результат);
			Контекст.ЗаписьНабора.ДатаИзмененияЗаписиРегистра = ТекущаяДатаСеанса();
			НаборЗаписей = Контекст.НаборЗаписей; // РегистрСведенийНаборЗаписей - 
			НаборЗаписей.Записать();
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Функция ПоказателиОбновленияУправляющегоПотока(Контекст)
	
	Если Не РегистрироватьПоказателиОбновленияДоступа() Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Показатели = Новый Структура;
	
	// Переменные.
	Показатели.Вставить("НачалоРаботыВМиллисекундах", ТекущаяУниверсальнаяДатаВМиллисекундах());
	Показатели.Вставить("НачалоВыдачиЗадания");
	
	Если Контекст.ОбновлениеВЭтомСеансе Тогда
		ДобавитьПоказателиВыполненияЗаданий(Показатели);
	Иначе
		Показатели.Вставить("ВремяОжиданийСвободногоПотока", 0);
		
		Показатели.Вставить("КоличествоВыданныхЗаданий", 0);
		Показатели.Вставить("ВремяВыдачиЗаданий", 0);
		Показатели.Вставить("МинимальноеВремяВыдачиЗадания", 0);
		Показатели.Вставить("МаксимальноеВремяВыдачиЗадания", 0);
		
		Показатели.Вставить("ВремяОбработкиРезультатовЗаданий", 0);
		Показатели.Вставить("МинимальноеВремяОбработкиРезультатаЗадания", 0);
		Показатели.Вставить("МаксимальноеВремяОбработкиРезультатаЗадания", 0);
		
		Показатели.Вставить("КоличествоПотоковСПревышениемВремениВыполнения", 0);
		Показатели.Вставить("КоличествоПотоковСНештатнымЗавершением", 0);
		
		// Переменные.
		Показатели.Вставить("НачалоОбработкиЗадания", 0);
	КонецЕсли;
	
	Возврат Показатели;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Функция ПоказателиОбновленияИсполняющегоПотока()
	
	Если Не РегистрироватьПоказателиОбновленияДоступа() Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Показатели = Новый Структура;
	
	Показатели.Вставить("НачалоРаботыВМиллисекундах", ТекущаяУниверсальнаяДатаВМиллисекундах());
	Показатели.Вставить("КоличествоОжиданийНовыхЗаданий", 0);
	Показатели.Вставить("ВремяОжиданияНовыхЗаданий", 0);
	
	ДобавитьПоказателиВыполненияЗаданий(Показатели);
	
	Возврат Показатели;
	
КонецФункции

// Для функций ПоказателиОбновленияУправляющегоПотока, ПоказателиОбновленияИсполняющегоПотока.
Процедура ДобавитьПоказателиВыполненияЗаданий(Показатели)
	
	// Переменные.
	Показатели.Вставить("ЗаданиеСПолучениемПорций", Истина);
	Показатели.Вставить("НачалоПервойПопыткиВыполненияЗадания", 0);
	Показатели.Вставить("НачалоВыполненияЗадания", 0);
	
	// Общие показатели.
	Показатели.Вставить("КоличествоЗаданийСПовторамиИзЗаОшибок", 0);
	Показатели.Вставить("ВремяВыполненияЗаданийСПовторамиИзЗаОшибок", 0);
	Показатели.Вставить("КоличествоПовторовЗаданийИзЗаОшибок", 0);
	Показатели.Вставить("МаксимальноеКоличествоПовторовЗаданияПриОшибке", 0);
	Показатели.Вставить("ТекстОшибокПриПопыткахПовтора", "");
	
	// Получение порций или сразу обработка маленькой порции.
	Показатели.Вставить("КоличествоВыполненныхЗаданийСПолучениемПорций", 0);
	Показатели.Вставить("ВремяВыполненияЗаданийСПолучениемПорций", 0);
	Показатели.Вставить("МинимальноеВремяВыполненияЗаданийСПолучениемПорций", 0);
	Показатели.Вставить("МаксимальноеВремяВыполненияЗаданийСПолучениемПорций", 0);
	
	// Только обработки порции.
	Показатели.Вставить("КоличествоВыполненныхЗаданийБезПолученияПорций", 0);
	Показатели.Вставить("ВремяВыполненияЗаданийБезПолученияПорций", 0);
	Показатели.Вставить("МинимальноеВремяВыполненияЗаданийБезПолученияПорций", 0);
	Показатели.Вставить("МаксимальноеВремяВыполненияЗаданийБезПолученияПорций", 0);
	
КонецПроцедуры

// Для функции ЗапуститьОбновлениеДоступаСписка.
Процедура СнятьПоказателиВыдачиЗаданий(Показатели)
	
	ВремяВыдачиЗадания = ТекущаяУниверсальнаяДатаВМиллисекундах() - Показатели.НачалоВыдачиЗадания;
	
	Показатели.КоличествоВыданныхЗаданий = Показатели.КоличествоВыданныхЗаданий + 1;
	Показатели.ВремяВыдачиЗаданий = Показатели.ВремяВыдачиЗаданий + ВремяВыдачиЗадания;
	
	Если Показатели.МинимальноеВремяВыдачиЗадания = 0 Тогда
		Показатели.МинимальноеВремяВыдачиЗадания = ВремяВыдачиЗадания;
	КонецЕсли;
	Если ВремяВыдачиЗадания < Показатели.МинимальноеВремяВыдачиЗадания Тогда
		Показатели.МинимальноеВремяВыдачиЗадания = ВремяВыдачиЗадания;
	КонецЕсли;
	
	Если ВремяВыдачиЗадания > Показатели.МаксимальноеВремяВыдачиЗадания Тогда
		Показатели.МаксимальноеВремяВыдачиЗадания = ВремяВыдачиЗадания;
	КонецЕсли;
	
КонецПроцедуры

// Для функции ЗапуститьОбновлениеДоступаСписка.
Процедура СнятьПоказателиОбработкиРезультатаЗадания(Показатели)
	
	ВремяОбработкиЗадания = ТекущаяУниверсальнаяДатаВМиллисекундах() - Показатели.НачалоОбработкиЗадания;
	
	Показатели.ВремяОбработкиРезультатовЗаданий =
		Показатели.ВремяОбработкиРезультатовЗаданий + ВремяОбработкиЗадания;
	
	Если Показатели.МинимальноеВремяОбработкиРезультатаЗадания = 0 Тогда
		Показатели.МинимальноеВремяОбработкиРезультатаЗадания = ВремяОбработкиЗадания;
	КонецЕсли;
	Если ВремяОбработкиЗадания < Показатели.МинимальноеВремяОбработкиРезультатаЗадания Тогда
		Показатели.МинимальноеВремяОбработкиРезультатаЗадания = ВремяОбработкиЗадания;
	КонецЕсли;
	
	Если ВремяОбработкиЗадания > Показатели.МаксимальноеВремяОбработкиРезультатаЗадания Тогда
		Показатели.МаксимальноеВремяОбработкиРезультатаЗадания = ВремяОбработкиЗадания;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Процедура СнятьПоказателиВыполненияЗадания(Показатели)
	
	ВремяВыполненияЗадания = ТекущаяУниверсальнаяДатаВМиллисекундах() - Показатели.НачалоВыполненияЗадания;
	
	Если Показатели.ЗаданиеСПолучениемПорций Тогда
		Показатели.КоличествоВыполненныхЗаданийСПолучениемПорций =
			Показатели.КоличествоВыполненныхЗаданийСПолучениемПорций + 1;
		
		Показатели.ВремяВыполненияЗаданийСПолучениемПорций =
			Показатели.ВремяВыполненияЗаданийСПолучениемПорций + ВремяВыполненияЗадания;
		
		Если Показатели.МинимальноеВремяВыполненияЗаданийСПолучениемПорций = 0 Тогда
			Показатели.МинимальноеВремяВыполненияЗаданийСПолучениемПорций = ВремяВыполненияЗадания;
		КонецЕсли;
		Если ВремяВыполненияЗадания < Показатели.МинимальноеВремяВыполненияЗаданийСПолучениемПорций Тогда
			Показатели.МинимальноеВремяВыполненияЗаданийСПолучениемПорций = ВремяВыполненияЗадания;
		КонецЕсли;
		
		Если ВремяВыполненияЗадания > Показатели.МаксимальноеВремяВыполненияЗаданийСПолучениемПорций Тогда
			Показатели.МаксимальноеВремяВыполненияЗаданийСПолучениемПорций = ВремяВыполненияЗадания;
		КонецЕсли;
	Иначе
		Показатели.КоличествоВыполненныхЗаданийБезПолученияПорций =
			Показатели.КоличествоВыполненныхЗаданийБезПолученияПорций + 1;
		
		Показатели.ВремяВыполненияЗаданийБезПолученияПорций =
			Показатели.ВремяВыполненияЗаданийБезПолученияПорций + ВремяВыполненияЗадания;
		
		Если Показатели.МинимальноеВремяВыполненияЗаданийБезПолученияПорций = 0 Тогда
			Показатели.МинимальноеВремяВыполненияЗаданийБезПолученияПорций = ВремяВыполненияЗадания;
		КонецЕсли;
		Если ВремяВыполненияЗадания < Показатели.МинимальноеВремяВыполненияЗаданийБезПолученияПорций Тогда
			Показатели.МинимальноеВремяВыполненияЗаданийБезПолученияПорций = ВремяВыполненияЗадания;
		КонецЕсли;
		
		Если ВремяВыполненияЗадания > Показатели.МаксимальноеВремяВыполненияЗаданийБезПолученияПорций Тогда
			Показатели.МаксимальноеВремяВыполненияЗаданийБезПолученияПорций = ВремяВыполненияЗадания;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Процедура СнятьПоказателиОшибокВыполненияЗадания(Показатели, ТекстОшибок, КоличествоОшибок);
	
	Если КоличествоОшибок = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Показатели.КоличествоЗаданийСПовторамиИзЗаОшибок = Показатели.КоличествоЗаданийСПовторамиИзЗаОшибок + 1;
	
	Показатели.ВремяВыполненияЗаданийСПовторамиИзЗаОшибок = Показатели.ВремяВыполненияЗаданийСПовторамиИзЗаОшибок
		+ (ТекущаяУниверсальнаяДатаВМиллисекундах() - Показатели.НачалоПервойПопыткиВыполненияЗадания);
	
	Показатели.КоличествоПовторовЗаданийИзЗаОшибок =
		Показатели.КоличествоПовторовЗаданийИзЗаОшибок + КоличествоОшибок;
	
	Если КоличествоОшибок > Показатели.МаксимальноеКоличествоПовторовЗаданияПриОшибке Тогда
		Показатели.МаксимальноеКоличествоПовторовЗаданияПриОшибке = КоличествоОшибок;
	КонецЕсли;
	
	ДобавитьТекстОшибкиЗавершения(Показатели.ТекстОшибокПриПопыткахПовтора, ТекстОшибок);
	
КонецПроцедуры

// Для процедур ЗарегистрироватьПоказателиОбновленияОсновногоПотока,
// ЗарегистрироватьПоказателиОбновленияИсполняющегоПотока,
// ДобавитьЗначенияПоказателейРаботыСеанса,
// ДобавитьЗначенияПоказателейВыполненияЗаданий.
//
Функция ФорматСекунд(ЧислоСекунд)
	
	Если ЧислоСекунд = 0 Тогда
		Возврат СтрЗаменить(Формат(1.111), "1", "0");
	КонецЕсли;
	
	Возврат Формат(ЧислоСекунд, "ЧДЦ=3; ЧГ=");
	
КонецФункции

// Для процедур ЗарегистрироватьПоказателиОбновленияОсновногоПотока,
// ЗарегистрироватьПоказателиОбновленияИсполняющегоПотока,
// ДобавитьЗначенияПоказателейВыполненияЗаданий.
//
Функция ФорматКоличества(ЧислоКоличества)
	
	Возврат Формат(ЧислоКоличества, "ЧН=0; ЧГ=");
	
КонецФункции

// Для процедуры ЗавершитьОбновлениеДоступа.
Процедура ЗарегистрироватьПоказателиОбновленияОсновногоПотока(Контекст)
	
	Показатели = Контекст.Показатели;
	ОписаниеСеанса = Контекст.ОписаниеОсновногоСеанса; // См. ОписаниеОсновногоСеанса
	
	Если Контекст.ОбновлениеВЭтомСеансе Тогда
		Комментарий = НСтр("ru = 'Завершен сеанс обновления доступа.'");
		ДобавитьЗначенияПоказателейРаботыСеанса(Комментарий, Показатели, ОписаниеСеанса);
		ДобавитьЗначенияПоказателейВыполненияЗаданий(Комментарий, Показатели);
	Иначе
		ВремяОжиданийСвободногоПотока = Показатели.ВремяОжиданийСвободногоПотока / 1000;
		
		ВремяВыдачиЗаданий             = Показатели.ВремяВыдачиЗаданий / 1000;
		МинимальноеВремяВыдачиЗадания  = Показатели.МинимальноеВремяВыдачиЗадания / 1000;
		МаксимальноеВремяВыдачиЗадания = Показатели.МаксимальноеВремяВыдачиЗадания / 1000;
		
		ВремяОбработкиРезультатовЗаданий            = Показатели.ВремяОбработкиРезультатовЗаданий / 1000;
		МинимальноеВремяОбработкиРезультатаЗадания  = Показатели.МинимальноеВремяОбработкиРезультатаЗадания / 1000;
		МаксимальноеВремяОбработкиРезультатаЗадания = Показатели.МаксимальноеВремяОбработкиРезультатаЗадания / 1000;
		
		Комментарий = НСтр("ru = 'Завершен сеанс управляющего потока обновления доступа.'");
		ДобавитьЗначенияПоказателейРаботыСеанса(Комментарий, Показатели, ОписаниеСеанса);
		
		Комментарий = Комментарий + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Время ожиданий свободного потока: %1 сек
			           |
			           |Количество выданных заданий: %2
			           |Время выдачи заданий: %3 сек
			           |Минимальное время выдачи заданий: %4 сек
			           |Максимальное время выдачи заданий: %5 сек'"),
			ФорматСекунд(ВремяОжиданийСвободногоПотока),
			ФорматКоличества(Показатели.КоличествоВыданныхЗаданий),
			ФорматСекунд(ВремяВыдачиЗаданий),
			ФорматСекунд(МинимальноеВремяВыдачиЗадания),
			ФорматСекунд(МаксимальноеВремяВыдачиЗадания));
		
		Комментарий = Комментарий + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Время обработки результатов заданий: %1 сек
			           |Минимальное время обработки результатов заданий: %2 сек
			           |Максимальное время обработки результатов заданий: %3 сек
			           |
			           |Количество потоков с превышением времени выполнения: %4
			           |Количество потоков с нештатным завершением: %5'"),
			ФорматСекунд(ВремяОбработкиРезультатовЗаданий),
			ФорматСекунд(МинимальноеВремяОбработкиРезультатаЗадания),
			ФорматСекунд(МаксимальноеВремяОбработкиРезультатаЗадания),
			ФорматКоличества(Показатели.КоличествоПотоковСПревышениемВремениВыполнения),
			ФорматКоличества(Показатели.КоличествоПотоковСНештатнымЗавершением));
	КонецЕсли;
	Данные = ОписаниеСеанса.Идентификатор;
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Управление доступом.Показатели.Обновление доступа'",
		     ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Информация, , Данные, Комментарий);
		
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Процедура ЗарегистрироватьПоказателиОбновленияИсполняющегоПотока(Контекст)
	
	Показатели = Контекст.Показатели;
	
	ВремяОжиданияНовыхЗаданий = Показатели.ВремяОжиданияНовыхЗаданий / 1000;
	
	Комментарий = НСтр("ru = 'Завершен сеанс исполняющего потока обновления доступа.'");
	ДобавитьЗначенияПоказателейРаботыСеанса(Комментарий, Показатели, Контекст.ТекущийСеанс);
	
	ОписаниеСеанса = Контекст.ОписаниеРодительскогоСеанса; // См. ОписаниеОсновногоСеанса
	
	Комментарий = Комментарий + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Номер сеанса управляющего потока: %1
		           |Начало сеанса управляющего потока: %2
		           |
		           |Количество ожиданий новых заданий: %3
		           |Время ожидания новых заданий: %4 сек'"),
		ОписаниеСеанса.НомерСеанса,
		ОписаниеСеанса.НачалоСеанса,
		ФорматКоличества(Показатели.КоличествоОжиданийНовыхЗаданий),
		ФорматСекунд(ВремяОжиданияНовыхЗаданий));
	
	ДобавитьЗначенияПоказателейВыполненияЗаданий(Комментарий, Показатели);
	Данные = ОписаниеСеанса.Идентификатор;
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Управление доступом.Показатели.Обновление доступа'",
		     ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Информация, , Данные, Комментарий);
		
КонецПроцедуры

// Для процедур ЗарегистрироватьПоказателиОбновленияУправляющегоПотока и
// ЗарегистрироватьПоказателиОбновленияИсполняющегоПотока.
//
Процедура ДобавитьЗначенияПоказателейРаботыСеанса(Комментарий, Показатели, ОписаниеСеанса)
	
	ВремяРаботы = (ТекущаяУниверсальнаяДатаВМиллисекундах()
		- Показатели.НачалоРаботыВМиллисекундах) / 1000;
	
	Комментарий = Комментарий + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Номер сеанса: %1
		           |Начало сеанса: %2
		           |Время работы: %3 сек'"),
		ОписаниеСеанса.НомерСеанса,
		ОписаниеСеанса.НачалоСеанса,
		ФорматСекунд(ВремяРаботы));
	
КонецПроцедуры

// Для процедур ЗарегистрироватьПоказателиОбновленияУправляющегоПотока и
// ЗарегистрироватьПоказателиОбновленияИсполняющегоПотока.
//
Процедура ДобавитьЗначенияПоказателейВыполненияЗаданий(Комментарий, Показатели)
	
	ВремяВыполненияЗаданийСПолучениемПорций              = Показатели.ВремяВыполненияЗаданийСПолучениемПорций / 1000;
	МаксимальноеВремяВыполненияЗаданийСПолучениемПорций  = Показатели.МаксимальноеВремяВыполненияЗаданийСПолучениемПорций / 1000;
	МинимальноеВремяВыполненияЗаданийСПолучениемПорций   = Показатели.МинимальноеВремяВыполненияЗаданийСПолучениемПорций / 1000;
	
	ВремяВыполненияЗаданийБезПолученияПорций             = Показатели.ВремяВыполненияЗаданийБезПолученияПорций / 1000;
	МаксимальноеВремяВыполненияЗаданийБезПолученияПорций = Показатели.МаксимальноеВремяВыполненияЗаданийБезПолученияПорций / 1000;
	МинимальноеВремяВыполненияЗаданийБезПолученияПорций  = Показатели.МинимальноеВремяВыполненияЗаданийБезПолученияПорций / 1000;
	
	КоличествоВыполненныхЗаданий = Показатели.КоличествоВыполненныхЗаданийСПолучениемПорций
		+ Показатели.КоличествоВыполненныхЗаданийБезПолученияПорций;
	
	ВремяВыполненияЗаданий = ВремяВыполненияЗаданийСПолучениемПорций + ВремяВыполненияЗаданийБезПолученияПорций;
	ВремяВыполненияЗаданийСПовторамиИзЗаОшибок = Показатели.ВремяВыполненияЗаданийСПовторамиИзЗаОшибок / 1000;
	
	Комментарий = Комментарий + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Количество выполненных заданий: %1
		           |Время выполнения заданий: %2 сек
		           |
		           |Количество заданий с повторами из-за ошибок: %3
		           |Время выполнения заданий с повторами из-за ошибок: %4 сек
		           |Количество повторов заданий из-за ошибок: %5
		           |Максимум повторов отдельного задания из-за ошибок: %6'"),
		ФорматКоличества(КоличествоВыполненныхЗаданий),
		ФорматСекунд(ВремяВыполненияЗаданий),
		ФорматКоличества(Показатели.КоличествоЗаданийСПовторамиИзЗаОшибок),
		ФорматСекунд(ВремяВыполненияЗаданийСПовторамиИзЗаОшибок),
		ФорматКоличества(Показатели.КоличествоПовторовЗаданийИзЗаОшибок),
		ФорматКоличества(Показатели.МаксимальноеКоличествоПовторовЗаданияПриОшибке));
	
	Комментарий = Комментарий + Символы.ПС + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Задания получения порций и/или обработки маленькой порции:
		           |- количество выполненных заданий: %1
		           |- время выполнения заданий: %2 сек
		           |- минимальное время выполнения задания: %3 сек
		           |- максимальное время выполнения задания: %4 сек
		           |
		           |Задания обработки порций:
		           |- количество выполненных заданий: %5
		           |- время выполнения заданий: %6 сек
		           |- минимальное время выполнения задания: %7 сек
		           |- максимальное время выполнения задания: %8 сек'"),
		ФорматКоличества(Показатели.КоличествоВыполненныхЗаданийСПолучениемПорций),
		ФорматСекунд(ВремяВыполненияЗаданийСПолучениемПорций),
		ФорматСекунд(МинимальноеВремяВыполненияЗаданийСПолучениемПорций),
		ФорматСекунд(МаксимальноеВремяВыполненияЗаданийСПолучениемПорций),
		ФорматКоличества(Показатели.КоличествоВыполненныхЗаданийБезПолученияПорций),
		ФорматСекунд(ВремяВыполненияЗаданийБезПолученияПорций),
		ФорматСекунд(МинимальноеВремяВыполненияЗаданийБезПолученияПорций),
		ФорматСекунд(МаксимальноеВремяВыполненияЗаданийБезПолученияПорций));
	
	Если ЗначениеЗаполнено(Показатели.ТекстОшибокПриПопыткахПовтора) Тогда
		Комментарий = Комментарий + Символы.ПС + Символы.ПС
			+ НСтр("ru = 'Тексты ошибок при попытках повторного выполнения:'")
			+ Символы.ПС + Символы.ПС + Показатели.ТекстОшибокПриПопыткахПовтора;
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ОбновлениеДоступаНаУровнеЗаписей, ОбработатьРезультатЗадания, ЗарегистрироватьОшибкуОбновленияДоступа.
Процедура ДобавитьТекстОшибкиЗавершения(ТекстОшибкиЗавершения, ТекстОшибки)
	
	Если Не ЗначениеЗаполнено(ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ТекстОшибкиЗавершения) Тогда
		Если СтрНайти(ТекстОшибкиЗавершения, ТекстОшибки) > 0 Тогда
			Возврат;
		КонецЕсли;
		ТекстОшибкиЗавершения = ТекстОшибкиЗавершения + Символы.ПС + Символы.ПС;
	КонецЕсли;
	
	ТекстОшибкиЗавершения = ТекстОшибкиЗавершения + ТекстОшибки;
	
КонецПроцедуры

// Для процедур ОбновитьСвойстваФоновогоЗадания, ОтменитьФоновоеЗаданиеПотока, ОбработатьВыполненныеЗадания,
// УдалитьОстановленныеПотоки, ВыполнитьОбновлениеДоступаСпискаВФоне.
//
Функция ТекстОшибкиОбновленияСКонтекстом(ИнформацияОбОшибке, ОбщиеПараметрыОбновления, УстранимаяОшибка = Ложь)
	
	Если ТипЗнч(ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Тогда
		ПредставлениеОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
	Иначе
		ПредставлениеОшибки = Строка(ИнформацияОбОшибке);
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ПредставлениеОшибки) Тогда
		Возврат "";
	КонецЕсли;
	
	Если ОбщиеПараметрыОбновления = Неопределено Тогда
		ТекстОшибки = ПредставлениеОшибки;
		
	ИначеЕсли Не ОбщиеПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		
		Если ОбщиеПараметрыОбновления.ДляВнешнихПользователей Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось обновить ключи доступа к элементам данных списка
				           |""%1"" (для внешних пользователей)
				           |по причине:
				           |%2'"),
				Строка(ОбщиеПараметрыОбновления.ИдентификаторСписка),
				ПредставлениеОшибки);
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось обновить ключи доступа к элементам данных списка
				           |""%1"" (для пользователей)
				           |по причине:
				           |%2'"),
				Строка(ОбщиеПараметрыОбновления.ИдентификаторСписка),
				ПредставлениеОшибки);
		КонецЕсли;
		
	Иначе // ОбновлениеКлючейДоступаПользователей.
		
		Если ОбщиеПараметрыОбновления.ДляВнешнихПользователей Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось обновить ключи доступа внешних пользователей списка
				           |""%1""
				           |по причине:
				           |%2'"),
				Строка(ОбщиеПараметрыОбновления.ИдентификаторСписка),
				ПредставлениеОшибки);
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось обновить ключи доступа пользователей списка
				           |""%1""
				           |по причине:
				           |%2'"),
				Строка(ОбщиеПараметрыОбновления.ИдентификаторСписка),
				ПредставлениеОшибки);
		КонецЕсли;
	КонецЕсли;
	
	Если УстранимаяОшибка Тогда
		ТекстОшибки = НСтр("ru = 'Возникла устранимая ошибка (обновление продолжается автоматически).'")
			+ Символы.ПС + ТекстОшибки;
	КонецЕсли;
	
	Возврат ТекстОшибки;
	
КонецФункции

// Для процедур ОбновлениеДоступаНаУровнеЗаписей, ЗавершитьФоновыеЗадания,
// ОбработатьРезультатВыполненногоЗадания, ОбновитьСвойстваФоновогоЗадания,
// ВыполнитьОбновлениеДоступаСпискаВФоне.
//
Процедура ЗарегистрироватьОшибкуОбновленияДоступа(ТекстОшибки, Контекст)
	
	Если Контекст.Свойство("ОписаниеРодительскогоСеанса") Тогда
		ОписаниеСеанса = Контекст.ОписаниеРодительскогоСеанса; // См. ОписаниеОсновногоСеанса
	Иначе
		ОписаниеСеанса = Контекст.ОписаниеОсновногоСеанса; // См. ОписаниеОсновногоСеанса
	КонецЕсли;
	
	Данные = ОписаниеСеанса.Идентификатор;
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Управление доступом.Обновление доступа на уровне записей'",
			ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Ошибка, , Данные, ТекстОшибки);
	
КонецПроцедуры

// Для функции ЗапуститьОбновлениеДоступаСписка и процедуры ВыполнитьОбновлениеДоступаСпискаВФоне.
Процедура ВыполнитьОбновлениеДоступаСпискаСПопыткамиПовтора(ОбщиеПараметрыОбновления, Контекст)
	
	Показатели = Контекст.Показатели;
	
	Если Показатели <> Неопределено Тогда
		Показатели.НачалоПервойПопыткиВыполненияЗадания = ТекущаяУниверсальнаяДатаВМиллисекундах();
	КонецЕсли;
	
	ПопыткаВыполнения = 1;
	ОшибкиПопытокВыполнения = Новый Массив;
	Пока Истина Цикл
		ТекстОшибкиТекущейПопытки = "";
		Если Показатели <> Неопределено Тогда
			Показатели.ЗаданиеСПолучениемПорций = Не ОбщиеПараметрыОбновления.Свойство("ПорцияИзНабора");
			Показатели.НачалоВыполненияЗадания = ТекущаяУниверсальнаяДатаВМиллисекундах();
		КонецЕсли;
		Попытка
			ВыполнитьОбновлениеДоступаСписка(ОбщиеПараметрыОбновления);
		Исключение
			Если ТранзакцияАктивна() Тогда
				ВызватьИсключение;
			КонецЕсли;
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ТекстОшибкиТекущейПопытки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		КонецПопытки;
		Если Показатели <> Неопределено И Не ЗначениеЗаполнено(ТекстОшибкиТекущейПопытки) Тогда
			СнятьПоказателиВыполненияЗадания(Показатели);
		КонецЕсли;
		ТекстОшибкиТребуетсяПерезапуск = "";
		Если СтандартныеПодсистемыСервер.ТребуетсяПерезапускСеанса(ТекстОшибкиТребуетсяПерезапуск) Тогда
			ОбщиеПараметрыОбновления.Вставить("ТребуетсяПерезапускСеанса", ТекстОшибкиТребуетсяПерезапуск);
			Если СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке) Тогда
				ТекстОшибкиТекущейПопытки = "";
			КонецЕсли;
		КонецЕсли;
		Если ЗначениеЗаполнено(ТекстОшибкиТекущейПопытки) Тогда
			ВремяОшибкиТекущейПопытки = Формат(ТекущаяДатаСеанса(), "ДЛФ=DT");
			ОшибкиПопытокВыполнения.Добавить(ВремяОшибкиТекущейПопытки + " " + ТекстОшибкиТекущейПопытки);
			ПопыткаВыполнения = ПопыткаВыполнения + 1;
			Если ПопыткаВыполнения < 9 И Не ЗначениеЗаполнено(ТекстОшибкиТребуетсяПерезапуск) Тогда
				Для Счетчик = 1 По ПопыткаВыполнения Цикл
					// @skip-check query-in-loop - Порционная обработка данных
					Если ОбновлениеДоступаОтменено() Тогда
						Прервать;
					КонецЕсли;
					Если Контекст.ТекущееФоновоеЗадание <> Неопределено Тогда
						Контекст.ТекущееФоновоеЗадание.ОжидатьЗавершенияВыполнения(1);
					Иначе
						ГраницаОжидания = ТекущаяУниверсальнаяДатаВМиллисекундах() + 1000;
						Пока ГраницаОжидания > ТекущаяУниверсальнаяДатаВМиллисекундах() Цикл
							Продолжить;
						КонецЦикла;
					КонецЕсли;
				КонецЦикла;
				// @skip-check query-in-loop - Порционная обработка данных
				Если Не ОбновлениеДоступаОтменено() Тогда
					Продолжить;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		ТекстОшибки = СтрСоединить(ОшибкиПопытокВыполнения, Символы.ПС + "---" + Символы.ПС);
		ТекстОшибки = ТекстОшибкиОбновленияСКонтекстом(ТекстОшибки, ОбщиеПараметрыОбновления);
		
		Если ЗначениеЗаполнено(ТекстОшибкиТекущейПопытки) Тогда
			ОбщиеПараметрыОбновления.Вставить("ТекстОшибкиЗавершения", "");
			ДобавитьТекстОшибкиЗавершения(ОбщиеПараметрыОбновления.ТекстОшибкиЗавершения, ТекстОшибки);
		КонецЕсли;
		Если Показатели <> Неопределено Тогда
			СнятьПоказателиОшибокВыполненияЗадания(Показатели, ТекстОшибки, ОшибкиПопытокВыполнения.Количество());
		КонецЕсли;
		Прервать;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСпискаСПопыткамиПовтора.
Процедура ВыполнитьОбновлениеДоступаСписка(ОбщиеПараметрыОбновления)
	
	Если Не ОбщиеПараметрыОбновления.Свойство("Кэш") Тогда
		ОбщиеПараметрыОбновления.Вставить("Кэш", Новый Структура);
	КонецЕсли;
	
	Если ОбщиеПараметрыОбновления.ИдентификаторСписка = Неопределено Тогда
		ОбъектМетаданных = Null;
	ИначеЕсли ОбщиеПараметрыОбновления.Свойство("Кэш")
	        И ОбщиеПараметрыОбновления.Кэш.Свойство("ОбъектыМетаданныхПоИдентификаторам") Тогда
		
		ОбъектМетаданных = ОбщиеПараметрыОбновления.Кэш.ОбъектыМетаданныхПоИдентификаторам.Получить(
			ОбщиеПараметрыОбновления.ИдентификаторСписка);
	Иначе
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(
			ОбщиеПараметрыОбновления.ИдентификаторСписка, Ложь);
	КонецЕсли;
	
	Если ОбъектМетаданных = Неопределено Тогда
		// Если расширение конфигурации отключено, тогда обновление невозможно,
		// но нельзя очищать регистрацию к обновлению.
		ОбщиеПараметрыОбновления.Вставить("НетЗаданий", "ОбъектМетаданныхОтключен");
		Возврат;
	КонецЕсли;
	
	ЭтоОбновлениеПрав = ОбщиеПараметрыОбновления.ЭтоОбновлениеПрав;
	
	Если Не ОбщиеПараметрыОбновления.ДляВнешнихПользователей
	   И ОбщиеПараметрыОбновления.ИдентификаторСписка
	       = Справочники.ИдентификаторыОбъектовМетаданных.ПустаяСсылка() Тогда
		УдалитьОбъектыНедопустимыхТиповВРегистреКлючиДоступаКОбъектам();
	КонецЕсли;
	
	ПараметрыОбновления = ПараметрыОбновления(ОбщиеПараметрыОбновления, ОбъектМетаданных);
	
	// Обработка ранее подготовленной порции для обработки.
	Если ОбщиеПараметрыОбновления.Свойство("ПорцияИзНабора") Тогда
		ПорцияИзНабора = ОбщиеПараметрыОбновления.ПорцияИзНабора; // См. ПорцияИзНабора
		ЭлементыПорции = ПорцияИзНабора.Элементы.Получить();
		ПараметрыОбновления.Вставить("ПоследнийОбновленныйЭлемент",
			ПорцияИзНабора.ПоследнийЭлементПорции);
		
		ОбновитьПорциюЭлементов(ЭлементыПорции, ПараметрыОбновления);
		
		ОбщиеПараметрыОбновления.Вставить("НовыйПоследнийОбновленныйЭлемент",
			ПараметрыОбновления.НовыйПоследнийОбновленныйЭлемент);
		
		Если ЭлементыПорции <> Неопределено Тогда
			ОбщиеПараметрыОбновления.ОбработкаЗавершена = Ложь;
			ВыбраныВсеЭлементы = ПорцияИзНабора.ПоследнийЭлементПорции.КлючДанных = Null;
			ОбщиеПараметрыОбновления.Вставить("НаборПорций", НаборПорцийЭлементов(
				ПараметрыОбновления, ЭлементыПорции, ВыбраныВсеЭлементы));
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	// Подготовка плана обработки элементов данных.
	ПодготовкаЗавершена = Ложь;
	ПараметрыОбновления.Вставить("ПерезапускОбновления", Ложь);
	
	Пока Не ПодготовкаЗавершена Цикл
		ПодготовкаЗавершена = Истина;
		ПараметрыОбновления.Вставить("ЕстьЗадания", Истина);
		ПараметрыОбновления.Вставить("ТочечноеЗадание", Неопределено);
		ПараметрыОбновления.Вставить("ПоследнийОбновленныйЭлемент", НачальныйЭлемент(ПараметрыОбновления));
		// @skip-check query-in-loop - Порционная обработка данных
		ПодготовитьПланОбновления(ПараметрыОбновления, ПодготовкаЗавершена);
	КонецЦикла;
	
	Если Не ПараметрыОбновления.ЕстьЗадания Тогда
		ОбщиеПараметрыОбновления.Вставить("НетЗаданий");
		Возврат;
	КонецЕсли;
	
	Если ПараметрыОбновления.ТочечноеЗадание <> Неопределено Тогда
		ВыбраныВсеЭлементы = Ложь;
		Элементы = ЭлементыТочечногоЗаданияДляОбновления(ПараметрыОбновления,
			КоличествоЭлементовВЗапросе(ЭтоОбновлениеПрав), ВыбраныВсеЭлементы);
		
		Если Элементы <> Неопределено Тогда
			МаксимумМиллисекунд = МинимальноеКоличествоСекундВыполненияТочечногоЗадания() * 1000;
			Если ПараметрыОбновления.ГраницаВремениОбработки - ТекущаяУниверсальнаяДатаВМиллисекундах() < МаксимумМиллисекунд Тогда
				ПараметрыОбновления.ГраницаВремениОбработки = ТекущаяУниверсальнаяДатаВМиллисекундах() + МаксимумМиллисекунд;
			КонецЕсли;
			ОбновитьПорциюЭлементов(Элементы, ПараметрыОбновления, Истина);
		КонецЕсли;
		Если Элементы <> Неопределено Или Не ВыбраныВсеЭлементы Тогда
			ПерезапуститьОбновлениеПриНезавершенномТочечномОбновлении(ПараметрыОбновления);
		КонецЕсли;
	КонецЕсли;
	
	УточнитьПоследнийОбновленныйЭлемент(ПараметрыОбновления);
	
	Если ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "НетДанных" Тогда
		ПараметрыОбновления.ПоследнийОбновленныйЭлемент.КлючДанных = Null;
		ЗаписатьПоследнийОбновленныйЭлемент(ОбщиеПараметрыОбновления,
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент);
		ОбщиеПараметрыОбновления.Вставить("НетЗаданий");
		Возврат;
	КонецЕсли;
	
	Если ПараметрыОбновления.ТочечноеЗадание <> Неопределено Тогда
		ПараметрыОбновления.ПоследнийОбновленныйЭлемент.Вставить("ОчиститьТочечноеЗадание");
		ЗаписатьПоследнийОбновленныйЭлемент(ОбщиеПараметрыОбновления,
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент);
		ПараметрыОбновления.ПоследнийОбновленныйЭлемент.Удалить("ОчиститьТочечноеЗадание");
	КонецЕсли;
	
	Если ПараметрыОбновления.ПерезапускОбновления Тогда
		ОбщиеПараметрыОбновления.Вставить("ПерезапускОбновления");
		Если ОбщиеПараметрыОбновления.Свойство("НовыйПоследнийЭлементПорции") Тогда
			ОбщиеПараметрыОбновления.Удалить("НовыйПоследнийЭлементПорции");
		КонецЕсли;
	КонецЕсли;
	
	Если ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления)
	   И Не ОбщиеПараметрыОбновления.ЭтоОбработкаУстаревшихЭлементов Тогда
		
		ЗаписатьПоследнийОбновленныйЭлемент(ОбщиеПараметрыОбновления,
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент);
		ОбщиеПараметрыОбновления.Вставить("НовыйПоследнийОбновленныйЭлемент",
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент);
		ОбщиеПараметрыОбновления.Вставить("НачальноеОбновлениеЗавершено");
		Возврат;
	КонецЕсли;
	
	Если ОбщиеПараметрыОбновления.ПолучитьПорции > 0
	   И ОбщиеПараметрыОбновления.Свойство("НовыйПоследнийЭлементПорции") Тогда
		
		ПараметрыОбновления.ПоследнийОбновленныйЭлемент = НачальныйЭлемент(ПараметрыОбновления);
		ЗаполнитьЗначенияСвойств(ПараметрыОбновления.ПоследнийОбновленныйЭлемент,
			ОбщиеПараметрыОбновления.НовыйПоследнийЭлементПорции);
	КонецЕсли;
	
	Если Не ЭтоОбновлениеПрав
	   И ПериодОбновленияДоПериодаДанных(ОбщиеПараметрыОбновления.ДатаНачала,
	         ПараметрыОбновления.ПоследнийОбновленныйЭлемент.Дата) Тогда
		
		ОбщиеПараметрыОбновления.Вставить("НовыйПоследнийОбновленныйЭлемент",
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент);
		
		ОбщиеПараметрыОбновления.ОбработкаЗавершена = Ложь;
		Возврат;
	КонецЕсли;
	
	// Однопоточное обновление некоторых наборов групп доступа.
	Если Не ЭтоОбновлениеПрав
	   И ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления)
	   И ПараметрыОбновления.ПоследнийОбновленныйЭлемент.КлючДанных = Неопределено Тогда
		
		Если ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "НовыеНаборыИзОдногоПользователя" Тогда
			УстранитьДублиНаборовИзОдногоПользователяВСправочнике(ПараметрыОбновления);
			
		ИначеЕсли ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "НаборыГруппДоступаНазначенныеПользователям" Тогда
			ЗаполнитьПустыеХешиНаборовГрупп(ПараметрыОбновления);
			
		ИначеЕсли ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "НовыеНаборыГруппСУстаревшимиПравами" Тогда
			Если Не ПараметрыОбновления.ДляВнешнихПользователей Тогда
				ОбновитьПраваНаРазрешенныйКлючДоступа();
			КонецЕсли;
			ОчиститьПраваПустогоНабораГруппДоступа(ПараметрыОбновления);
			
		ИначеЕсли ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
			ОчиститьПраваНесуществующихНаборовГруппДоступа(ПараметрыОбновления);
		КонецЕсли;
	КонецЕсли;
	
	// Запрос элементов для обработки.
	ВыбраныВсеЭлементы = Ложь;
	
	Если ОбщиеПараметрыОбновления.ПолучитьПорции > 0 Тогда
		КоличествоВПорции = КоличествоЭлементовВПорции(ПараметрыОбновления);
		КоличествоВЗапросе = КоличествоВПорции * ОбщиеПараметрыОбновления.ПолучитьПорции;
		Элементы = ЭлементыДляОбновления(ПараметрыОбновления, КоличествоВЗапросе, ВыбраныВсеЭлементы);
		
		Если ОбщиеПараметрыОбновления.Свойство("НовыйПоследнийЭлементПорции")
		 Или Элементы <> Неопределено
		   И Элементы.Количество() > КоличествоВПорции * 2 Тогда
			
			ОбщиеПараметрыОбновления.ОбработкаЗавершена = Ложь;
			ОбщиеПараметрыОбновления.Вставить("НаборПорций", НаборПорцийЭлементов(
				ПараметрыОбновления, Элементы, ВыбраныВсеЭлементы, КоличествоВПорции));
			Возврат;
		КонецЕсли;
	Иначе
		Возврат;
	КонецЕсли;
	
	// Обработка элементов данных.
	ПараметрыОбновления.Вставить("НовыйПоследнийОбновленныйЭлемент",
		НачальныйЭлемент(ПараметрыОбновления, , Истина));
	
	ОбщиеПараметрыОбновления.Вставить("НачальноеОбновлениеЗавершено");
	
	Если Элементы <> Неопределено Тогда
		ОбновитьПорциюЭлементов(Элементы, ПараметрыОбновления);
	КонецЕсли;
	
	// Уточнение нового последнего элемента.
	Если Элементы = Неопределено И ВыбраныВсеЭлементы Тогда
		УстановитьПустойПоследнийЭлемент(ПараметрыОбновления.НовыйПоследнийОбновленныйЭлемент,
			ПараметрыОбновления);
	КонецЕсли;
	
	// Запись нового последнего элемента.
	ЗаписатьПоследнийОбновленныйЭлемент(ОбщиеПараметрыОбновления,
		ПараметрыОбновления.НовыйПоследнийОбновленныйЭлемент);
	
	Если ПараметрыОбновления.НовыйПоследнийОбновленныйЭлемент.КлючДанных = Null Тогда
		ОбщиеПараметрыОбновления.Вставить("НетЗаданий");
		Возврат;
	КонецЕсли;
	
	ОбщиеПараметрыОбновления.Вставить("НовыйПоследнийОбновленныйЭлемент",
		ПараметрыОбновления.НовыйПоследнийОбновленныйЭлемент);
	
	// Подготовка оставшихся элементов к продолжению обновления.
	Если Элементы <> Неопределено Тогда
		ОбщиеПараметрыОбновления.Вставить("НаборПорций",
			НаборПорцийЭлементов(ПараметрыОбновления, Элементы, ВыбраныВсеЭлементы));
	КонецЕсли;
	
КонецПроцедуры

Функция ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления)
	
	Если ПараметрыОбновления.Свойство("Список") Тогда
		Возврат ПараметрыОбновления.Список = "Справочник.НаборыГруппДоступа";
	КонецЕсли;
	
	Возврат ПараметрыОбновления.ИдентификаторСписка
		= ПараметрыОбновления.ИдентификаторСправочникаНаборыГруппДоступа;
	
КонецФункции

// Для процедура ВыполнитьОбновлениеДоступаСписка.
//
// Возвращаемое значение:
//   Структура:
//     * ЭтоОбновлениеПрав        - Булево
//     * ИдентификаторСписка      - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//                                - СправочникСсылка.ИдентификаторыОбъектовРасширений
//     * ДляВнешнихПользователей  - Булево
//     * Кэш                      - см. НовыйКэшКонтекста
//     * ДатаНачала               - Дата
//     * ДатаОкончания            - Дата
//     * МаксимумПорцийИзИсходной - Число
//     * ИдентификаторСправочникаНаборыГруппДоступа - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//     * ГраницаВремениОбработки  - Дата
//     
//    Свойства, если ЭтоСправочникНаборыГруппДоступа.
//     * СписокСДатой    - Булево
//     * СписокСПериодом - Булево
//     * ЭтоСсылочныйТип - Булево
//     * ПустойНаборГруппДоступа - СправочникСсылка.НаборыГруппДоступа
//   
//    Свойства, если не ЭтоСправочникНаборыГруппДоступа.
//     * ИдентификаторТранзакции - УникальныйИдентификатор
//     * ЗависимыеСпискиПоКлючамДоступа - Массив из Строка - полные имена списков
//     
//    Свойства добавляемые в процессе работы.
//     * ЕстьИзмененияПрав - Булево
//     * ОбновитьПраваНаКлючи - Булево
//     * ПоследнийОбновленныйЭлемент - см. НачальныйЭлемент
//     * НовыйПоследнийОбновленныйЭлемент - см. НачальныйЭлемент
//     * ПерезапускОбновления - Булево
//     * ЕстьЗадания - Булево
//     * ТочечноеЗадание - см. ПодготовленноеТочечноеЗадание
//     * НаборПорций - Массив из см. ПорцияИзНабора
//     * ТипПользователя - Тип
//     * ТипГруппыПользователей - Тип
//     * ТипГруппыДоступа - Тип
//     * ПустаяГруппаДоступа - СправочникСсылка.ГруппыДоступа
//     * ПраваГруппДоступаСписка               - см. НовыеПраваГруппДоступаСписка
//     * ПользователиГруппПользователей        - см. НовыеПользователиГруппПользователей
//     * ГруппыПользователейКакЗначенияДоступа - см. НовыеГруппыПользователейКакЗначенияДоступа
//     * УчастникиГруппДоступа                 - см. НовыеУчастникиГруппДоступа
//     * ГруппыПользователейГруппДоступа       - см. НовыеГруппыПользователейГруппДоступа
//     * ЗначенияГруппДоступа                  - см. НовыеЗначенияГруппДоступа
//     * РолиПрофилейГруппДоступа              - см. НовыеРолиПрофилейГруппДоступа
//     * ГруппыДоступаПрофилей                 - см. НовыеГруппыДоступаПрофилей
//     * ПраваНаСпискиВедущихКлючейДоступа     - см. НовыеПраваНаСпискиВедущихКлючейДоступа
//     * ПраваНаВедущиеКлючиДоступа            - см. НовыеПраваНаВедущиеКлючиДоступа
//     * ПраваНаВедущиеСписки                  - см. НовыеПраваНаВедущиеСписки
//     * ПраваПоВладельцамНастроекПрав         - см. НовыеПраваПоВладельцамНастроекПрав
//     * МодельОбъектовВПамяти - см. МодельОбъектовВПамяти
//   
//    Копия свойств из ПараметрыОграниченияПоСтруктуреОграничения.
//     * Список                  - Строка
//     * ДляВнешнихПользователей - Булево
//     * Версия                  - Строка
//     * ВедущиеСписки           - см. НовыеВедущиеСписки
//     * ДоступЗапрещен          - Булево
//     * ОграничениеОтключено    - Булево
//     * ОграничениеЧтенияОтключено - Булево
//     * ПолеВладельца - см. НовоеПолеВладельца
//     * ТребуетсяОграничениеПоВладельцу    - Булево
//     * ИспользуетсяОграничениеПоВладельцу - Булево
//     * РассчитыватьПраваПользователей     - Булево
//     * ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа
//     * ЧтениеРазрешеноДляВсехПользователей - Булево
//     * ИзменениеРазрешеноДляВсехПользователей - Булево
//     * ЕстьВедущиеКлючиДоступа                - Булево
//     * ЕстьВедущиеСпискиПоПравам              - Булево
//     * ЕстьФункцияПравоДоступаИлиРольДоступна - Булево
//     * ТипыВладельцевНастроекПрав        - ФиксированноеСоответствие
//     * ИдентификаторТаблицыНастроекПрав  - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//     * ЕстьВладельцыНастроекПрав         - Булево
//     * ИспользуемыеТипыЗначенийДоступа   - Массив из Тип
//     * ВсеВидыОграниченийПрав            - Соответствие
//     * ПоляТаблицОбъекта                 - Массив из см. НовыеПоляТаблицыОбъекта
//     * ИмяОтдельногоРегистраКлючей       - Строка
//     * ОпорныеПоля                       - см. НовоеОписаниеОпорныхПолей
//     * ВариантДоступа                    - Число
//     * СоставПолей                       - Число
//     * ЕстьОграничениеЧтения             - Булево
//     * ЕстьОграничениеИзменения          - Булево
//     * ЕстьОграничениеПоПользователям    - Булево
//     * СтруктураРасчетаПраваЧтение       - см. СтруктураРасчетаПрава
//     * СтруктураРасчетаПраваИзменение    - см. СтруктураРасчетаПрава
//     * Контекст                          - см. КонтекстПараметровПоСтруктуреОграничения
//
//    Свойства, добавляемые в процедуре ДобавитьТекстыЗапросовВПараметрыОграничения.
//     * ТекстЗапросаПроверкиПравЧтениеИзменение - Строка
//     * ТекстЗапросаПроверкиПраваЧтение - Строка
//     * ПолеОбъектаВладельцаВЗапросеПроверкиПрав - Строка
//     * ТекстЗапросаУстаревшихЭлементовДанных - Строка
//     * ТаблицыКлюча - Массив из Строка
//     * РеквизитыТаблицКлюча - см. НовыеРеквизитыТаблицКлюча
//     * ТекстЗапросаЭлементовДанныхСУстаревшимиКлючами - Строка
//     * ТекстЗапросаЭлементовДанныхБезКлючейДоступа - Строка
//     * ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейСуществующихЗаписей - Строка
//     * ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейНовыхЗаписей - Строка
//     * ОписаниеЗапросовУстаревшихКлючейДоступаПоВедущимОбъектам - Соответствие из КлючИЗначение:
//        ** Ключ - Строка
//        ** Значение - Соответствие из КлючИЗначение:
//                       *** Ключ - Строка
//                       *** Значение - Соответствие
//                    - Структура:
//                       *** ТипСсылки - ХранилищеЗначения
//                       *** КлючиЗапросовПоТипам - Соответствие
//                       *** ТекстыЗапросовПоКлючам - Соответствие
//                       *** ТекстЗапросаПараметров - Строка
//                    - Строка
//     * ТекстЗапросаТекущихКлючейДоступаРегистра - Строка
//     * ТекстЗапросаЗначенийЭлементовДанныхДляКлючейДоступа - Строка
//     * ТекстЗапросаЗначенийОбъектовВПамятиДляКлючейДоступа - Строка
//     * ТекстЗапросаЗначенийИзИспользуемыхКлючейДоступаДляСравнения - Строка
//     * ТекстЗапросаЗначенийИзВсехКлючейДоступаДляСравнения - Строка
//     * ТекстЗапросаСуществованияКлючейДляСравнения - Строка
//     * ТекстЗапросаКлючейДоступаДляОбновленияПрав - Строка
//     * ТекстЗапросаКлючейПоВедущимКлючамДляОбновленияПрав - Строка
//     * ТекстЗапросаЗначенийИзКлючейДоступаДляРасчетаПрав - Строка
//     * ТекстЗапросаУстаревшихКлючейДоступа - Строка
//
Функция ПараметрыОбновления(ОбщиеПараметрыОбновления, ОбъектМетаданных)
	
	ПараметрыОбновления = Новый Структура("ЭтоОбновлениеПрав,
		|ИдентификаторСписка, ДляВнешнихПользователей, Кэш,
		|ДатаНачала, ДатаОкончания, МаксимумПорцийИзИсходной,
		|ЭтоФоновоеОбновлениеДоступа, ИдентификаторСправочникаНаборыГруппДоступа");
	
	ЗаполнитьЗначенияСвойств(ПараметрыОбновления, ОбщиеПараметрыОбновления);
	
	Если ТипЗнч(ОбъектМетаданных) = Тип("ОбъектМетаданных") Тогда
		ПараметрыОбновления.Вставить("Список", ОбъектМетаданных.ПолноеИмя());
	КонецЕсли;

	ПараметрыОбновления.Вставить("ГраницаВремениОбработки",
		ТекущаяУниверсальнаяДатаВМиллисекундах()
		+ ОбщиеПараметрыОбновления.МаксимумМиллисекундОбработки);
	
	ДобавитьПараметрыОграничения(ПараметрыОбновления);
	
	Возврат ПараметрыОбновления;
	
КонецФункции

// Для процедура ВыполнитьОбновлениеДоступаСписка.
Процедура ДобавитьПараметрыОграничения(ПараметрыОбновления)
	
	Если ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления) Тогда
		ПараметрыОбновления.Вставить("СписокСДатой", Ложь);
		ПараметрыОбновления.Вставить("СписокСПериодом", Ложь);
		ПараметрыОбновления.Вставить("ЭтоСсылочныйТип", Истина);
		ПараметрыОбновления.Вставить("ПустойНаборГруппДоступа", Справочники.НаборыГруппДоступа.ПустаяСсылка());
		Возврат;
	КонецЕсли;
	
	ИдентификаторТранзакции = Новый УникальныйИдентификатор;
	Список = ?(ПараметрыОбновления.Свойство("Список"), ПараметрыОбновления.Список, "");
	СвойстваСпискаКакВедущего = СвойстваСпискаКакВедущего(Список, ИдентификаторТранзакции);
	ПараметрыОграничения = ПараметрыОграничения(Список,
		ИдентификаторТранзакции, ПараметрыОбновления.ДляВнешнихПользователей);
	
	ПараметрыОграничения = Новый Структура(ПараметрыОграничения);
	Для Каждого КлючИЗначение Из ПараметрыОбновления Цикл
		ПараметрыОграничения.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	ПараметрыОбновления = ПараметрыОграничения;
	ПараметрыОбновления.Вставить("ИдентификаторТранзакции", ИдентификаторТранзакции);
	
	ИмяСвойстваВидаПользователей = ?(ПараметрыОбновления.ДляВнешнихПользователей,
		"ДляВнешнихПользователей", "ДляПользователей");
	
	Если СвойстваСпискаКакВедущего = Неопределено
	 Или СвойстваСпискаКакВедущего.ПоКлючамДоступа = Неопределено
	 Или СвойстваСпискаКакВедущего.ПоКлючамДоступа[ИмяСвойстваВидаПользователей] = Неопределено Тогда
		
		ПараметрыОбновления.Вставить("ЗависимыеСпискиПоКлючамДоступа", Новый Массив);
	Иначе
		ПараметрыОбновления.Вставить("ЗависимыеСпискиПоКлючамДоступа",
			СвойстваСпискаКакВедущего.ПоКлючамДоступа[ИмяСвойстваВидаПользователей]);
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ВыполнитьОбновлениеДоступаСписка, ПодготовитьПланОбновления.
// 
// Возвращаемое значение:
//  Структура:
//    * КлючДанных                               - ЛюбаяСсылка
//                                               - Null
//                                               - Структура
//    * ОбработатьУстаревшиеЭлементы             - Булево
//    * ОбработатьНаборыГруппСУстаревшимиПравами - Булево
//    * Дата                                     - Дата
//
Функция НачальныйЭлемент(ПараметрыОбновления, ВидКлючаДанных = Неопределено,
			СохранитьВидКлючаДанных = Ложь, ПерезапускОбновленияСНачала = Ложь)
	
	НачальныйЭлемент = Новый Структура;
	НачальныйЭлемент.Вставить("КлючДанных");
	НачальныйЭлемент.Вставить("ОбработатьУстаревшиеЭлементы", Ложь);
	НачальныйЭлемент.Вставить("ОбработатьНаборыГруппСУстаревшимиПравами", Ложь);
	СохранитьСвойства = Ложь;
	
	Если ВидКлючаДанных = Неопределено Тогда
		Если СохранитьВидКлючаДанных Тогда
			ВидКлючаДанных = ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных;
			СохранитьСвойства = Истина;
			
		ИначеЕсли ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления) Тогда
			ВидКлючаДанных = "НовыеНаборыИзОдногоПользователя";
			
		ИначеЕсли ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
			ВидКлючаДанных = "ЭлементыСУстаревшимиПравами";
		Иначе
			ВидКлючаДанных = "ЭлементыСУстаревшимиКлючами";
		КонецЕсли;
	ИначеЕсли ВидКлючаДанных <> "НетДанных"
	        И ПараметрыОбновления.Свойство("ПоследнийОбновленныйЭлемент") Тогда
		СохранитьСвойства = Истина;
	КонецЕсли;
	Если СохранитьСвойства Тогда
		Если ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ОбработатьУстаревшиеЭлементы Тогда
			НачальныйЭлемент.ОбработатьУстаревшиеЭлементы = Истина;
		КонецЕсли;
		Если ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ОбработатьНаборыГруппСУстаревшимиПравами Тогда
			НачальныйЭлемент.ОбработатьНаборыГруппСУстаревшимиПравами = Истина;
		КонецЕсли;
	КонецЕсли;
	УстановитьВидКлючаДанных(НачальныйЭлемент, ВидКлючаДанных);
	
	Если Не ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		НачальныйЭлемент.Вставить("Дата", ?(ПерезапускОбновленияСНачала,
			МаксимальнаяДата(), МаксимальнаяДатаПриПродолжении()));
	КонецЕсли;
	
	Возврат НачальныйЭлемент;
	
КонецФункции

// Для процедура ВыполнитьОбновлениеДоступаСписка.
Процедура ПодготовитьПланОбновления(ПараметрыОбновления, ПодготовкаЗавершена)
	
	ИдентификаторСписка     = ПараметрыОбновления.ИдентификаторСписка;
	ДляВнешнихПользователей = ПараметрыОбновления.ДляВнешнихПользователей;
	ЭтоОбновлениеПрав       = ПараметрыОбновления.ЭтоОбновлениеПрав;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Список", ИдентификаторСписка);
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ДляВнешнихПользователей);
	
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	КлючиУникальности.КлючУникальности КАК КлючУникальности,
	|	КлючиУникальности.ПараметрыЗадания КАК ПараметрыЗадания,
	|	КлючиУникальности.ДатаПоследнегоОбновленногоЭлемента КАК ДатаПоследнегоОбновленногоЭлемента
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК КлючиУникальности
	|ГДЕ
	|	КлючиУникальности.Список = &Список
	|	И КлючиУникальности.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|
	|УПОРЯДОЧИТЬ ПО
	|	КлючиУникальности.Список,
	|	КлючиУникальности.ДляВнешнихПользователей,
	|	КлючиУникальности.КлючУникальности";
	
	Если ЭтоОбновлениеПрав Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"РегистрСведений.ОбновлениеКлючейДоступаКДанным",
			"РегистрСведений.ОбновлениеКлючейДоступаПользователей");
		Запрос.Текст = СтрЗаменить(Запрос.Текст, ",
			|	КлючиУникальности.ДатаПоследнегоОбновленногоЭлемента КАК ДатаПоследнегоОбновленногоЭлемента", // @query-part-1
			"");
	КонецЕсли;
	
	РезультатЗапроса = Запрос.Выполнить();
	
	Если РезультатЗапроса.Пустой() Тогда
		ПараметрыОбновления.ЕстьЗадания = Ложь;
		Возврат;
	КонецЕсли;
	
	Выгрузка = РезультатЗапроса.Выгрузить();
	
	Если Выгрузка.Количество() = 1000 Тогда
		ПодготовкаЗавершена = Ложь;
	КонецЕсли;
	
	Если ЭтоОбновлениеПрав Тогда
		ПланОбновления = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаПользователей);
	Иначе
		ПланОбновления = СлужебныйНаборЗаписей(РегистрыСведений.ОбновлениеКлючейДоступаКДанным);
	КонецЕсли;
	
	Если ПараметрыОбновления.Список = СписокДляПланированияОбновленияКэшаРасчетаПрав() Тогда
		ОбработатьПланОбновленияКэшаРасчетаПрав(Выгрузка);
		ОчиститьЗагруженныеЗаписи(ПланОбновления, Выгрузка);
		ПараметрыОбновления.ЕстьЗадания = Ложь;
		Возврат;
	КонецЕсли;
	
	ПерезапускОбновления = Ложь;
	ПерезапускОбновленияСНачала = Ложь;
	СохраняемыеПараметрыЗадания = СохраняемыеПараметрыЗадания(ЭтоОбновлениеПрав, ПараметрыОбновления);
	
	Если Не ЗначениеЗаполнено(Выгрузка[0].КлючУникальности) Тогда
		ТекущийИтог = Выгрузка[0];
		ПараметрыЗадания = ТекущийИтог.ПараметрыЗадания.Получить();
		Если ТипЗнч(ПараметрыЗадания) = Тип("Структура") Тогда
			СохраняемыеПараметрыЗадания = СохраняемыеПараметрыЗадания(ЭтоОбновлениеПрав,
				ПараметрыОбновления, ПараметрыЗадания, ПерезапускОбновления);
		Иначе
			ПерезапускОбновления = Истина;
		КонецЕсли;
		Если Не ПерезапускОбновления И Не ЭтоОбновлениеПрав Тогда
			СохраненнаяДата = ТекущийИтог.ДатаПоследнегоОбновленногоЭлемента;
			Если ТипЗнч(СохраненнаяДата) = Тип("Дата") Тогда
				СохраняемыеПараметрыЗадания.ПоследнийОбновленныйЭлемент.Дата = СохраненнаяДата;
			Иначе
				ПерезапускОбновления = Истина;
			КонецЕсли;
		КонецЕсли;
		Выгрузка.Удалить(0);
		Если Не ПерезапускОбновления Тогда
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент =
				СохраняемыеПараметрыЗадания.ПоследнийОбновленныйЭлемент;
		КонецЕсли;
	КонецЕсли;
	
	Если Не ПерезапускОбновления
	   И ТекущийИтог <> Неопределено
	   И Выгрузка.Количество() = 0 Тогда
		
		ПараметрыОбновления.ТочечноеЗадание = ПодготовленноеТочечноеЗадание(
			ЭтоОбновлениеПрав, СохраняемыеПараметрыЗадания.ТочечноеЗадание, ПерезапускОбновления);
		Если Не ПерезапускОбновления Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	ПустойИдентификатор = ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор();
	ПолноеИмяРегистра = ?(ЭтоОбновлениеПрав, "РегистрСведений.ОбновлениеКлючейДоступаПользователей",
		"РегистрСведений.ОбновлениеКлючейДоступаКДанным");
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяРегистра);
	ЭлементБлокировки.УстановитьЗначение("Список",                  ИдентификаторСписка);
	ЭлементБлокировки.УстановитьЗначение("ДляВнешнихПользователей", ДляВнешнихПользователей);
	ЭлементБлокировки.УстановитьЗначение("КлючУникальности",        ПустойИдентификатор);
	
	Для Каждого Строка Из Выгрузка Цикл
		ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяРегистра);
		ЭлементБлокировки.УстановитьЗначение("Список",                  ИдентификаторСписка);
		ЭлементБлокировки.УстановитьЗначение("ДляВнешнихПользователей", ДляВнешнихПользователей);
		ЭлементБлокировки.УстановитьЗначение("КлючУникальности",        Строка.КлючУникальности);
		Прервать;
	КонецЦикла;
	
	МаксимальнаяДата = МаксимальнаяДата();
	СвойстваКлючаДанных = НачальныйЭлемент(ПараметрыОбновления, "НетДанных");
	ВидКлючаДанныхУстановлен = Истина;
	Для Каждого Строка Из Выгрузка Цикл
		ПараметрыЗадания = Строка.ПараметрыЗадания.Получить();
		Если ТипЗнч(ПараметрыЗадания) = Тип("Структура") И ПараметрыЗадания.Свойство("ТочечноеЗадание") Тогда
			ДобавитьВедущийОбъектКТочечномуЗаданию(ПараметрыЗадания.ТочечноеЗадание,
				СохраняемыеПараметрыЗадания.ТочечноеЗадание, ПерезапускОбновления);
		Иначе
			ПерезапускОбновления = Истина;
			Если Не ЭтоОбновлениеПрав И Строка.ДатаПоследнегоОбновленногоЭлемента = МаксимальнаяДата Тогда
				ПерезапускОбновленияСНачала = Истина;
			КонецЕсли;
			Если ЕстьСвойстваКлючаДанных(ПараметрыЗадания) Тогда
				Если СвойстваКлючаДанных.ПорядокВидаКлючаДанных > ПараметрыЗадания.ПорядокВидаКлючаДанных Тогда
					СвойстваКлючаДанных.ПорядокВидаКлючаДанных = ПараметрыЗадания.ПорядокВидаКлючаДанных;
					СвойстваКлючаДанных.ВидКлючаДанных         = ПараметрыЗадания.ВидКлючаДанных;
				КонецЕсли;
				Если ЭтоОбработкаУстаревшихЭлементов(Новый Структура("ПоследнийОбновленныйЭлемент", ПараметрыЗадания)) Тогда
					СвойстваКлючаДанных.ОбработатьУстаревшиеЭлементы = Истина;
				КонецЕсли;
				Если ПараметрыЗадания.Свойство("ОбработатьНаборыГруппСУстаревшимиПравами")
				   И ПараметрыЗадания.ОбработатьНаборыГруппСУстаревшимиПравами = Истина Тогда
					СвойстваКлючаДанных.ОбработатьНаборыГруппСУстаревшимиПравами = Истина;
				КонецЕсли;
			Иначе
				ВидКлючаДанныхУстановлен = Ложь;
				Если ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления) Тогда
					СвойстваКлючаДанных.ОбработатьНаборыГруппСУстаревшимиПравами = Истина;
				КонецЕсли;
				Если ЭтоОбновлениеПрав Или ПерезапускОбновленияСНачала Тогда
					Прервать;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	ПараметрыОбновления.ТочечноеЗадание = ПодготовленноеТочечноеЗадание(
		ЭтоОбновлениеПрав, СохраняемыеПараметрыЗадания.ТочечноеЗадание, ПерезапускОбновления);
	
	Если ПерезапускОбновления Тогда
		ПараметрыОбновления.ПерезапускОбновления = Истина;
		Если ПерезапускОбновленияСНачала Тогда
			ПараметрыОбновления.Вставить("ПерезапускОбновленияСНачала");
		КонецЕсли;
		ПараметрыОбновления.ПоследнийОбновленныйЭлемент =
			НачальныйЭлемент(ПараметрыОбновления, , , ПерезапускОбновленияСНачала);
	КонецЕсли;
	
	ПоследнийОбновленныйЭлемент = ПараметрыОбновления.ПоследнийОбновленныйЭлемент;
	Если ВидКлючаДанныхУстановлен Тогда
		Если ТекущийИтог = Неопределено Тогда
			УстановитьВидКлючаДанных(ПоследнийОбновленныйЭлемент, СвойстваКлючаДанных.ВидКлючаДанных);
			
		ИначеЕсли ПоследнийОбновленныйЭлемент.ПорядокВидаКлючаДанных > СвойстваКлючаДанных.ПорядокВидаКлючаДанных  Тогда
			ПоследнийОбновленныйЭлемент.ПорядокВидаКлючаДанных = СвойстваКлючаДанных.ПорядокВидаКлючаДанных;
			ПоследнийОбновленныйЭлемент.ВидКлючаДанных         = СвойстваКлючаДанных.ВидКлючаДанных;
		КонецЕсли;
	КонецЕсли;
	Если СвойстваКлючаДанных.ОбработатьУстаревшиеЭлементы Тогда
		ПоследнийОбновленныйЭлемент.ОбработатьУстаревшиеЭлементы = Истина;
	КонецЕсли;
	Если СвойстваКлючаДанных.ОбработатьНаборыГруппСУстаревшимиПравами Тогда
		ПоследнийОбновленныйЭлемент.ОбработатьНаборыГруппСУстаревшимиПравами = Истина;
	КонецЕсли;
	СохраняемыеПараметрыЗадания.ПоследнийОбновленныйЭлемент = ПоследнийОбновленныйЭлемент;
	
	Если ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "НетДанных" Тогда
		РазмерЗадания = 1;
	ИначеЕсли ЭтоОбработкаУстаревшихЭлементов(Новый Структура("ПоследнийОбновленныйЭлемент",
					ПоследнийОбновленныйЭлемент)) Тогда
		РазмерЗадания = 2;
	Иначе
		РазмерЗадания = 3;
	КонецЕсли;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "ВЫБРАТЬ ПЕРВЫЕ 1000", "ВЫБРАТЬ ПЕРВЫЕ 1"); // @query-part-1 @query-part-2
	ЗаданияУдалены = Ложь;
	
	ПланОбновления.Отбор.Список.Установить(ИдентификаторСписка);
	ПланОбновления.Отбор.ДляВнешнихПользователей.Установить(ДляВнешнихПользователей);
	ПланОбновления.Отбор.КлючУникальности.Установить(ПустойИдентификатор);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		РезультатЗапроса = Запрос.Выполнить();
		Если РезультатЗапроса.Пустой() Тогда
			ЗаданияУдалены = Истина;
		Иначе
			Запись = ПланОбновления.Добавить();
			Запись.Список                  = ИдентификаторСписка;
			Запись.ДляВнешнихПользователей = ДляВнешнихПользователей;
			Запись.ТочечноеЗадание         = ПараметрыОбновления.ТочечноеЗадание <> Неопределено;
			Запись.ПараметрыЗадания        = Новый ХранилищеЗначения(СохраняемыеПараметрыЗадания);
			Запись.РазмерЗадания           = РазмерЗадания;
			Если Не ЭтоОбновлениеПрав Тогда
				Запись.ДатаПоследнегоОбновленногоЭлемента =
					ПараметрыОбновления.ПоследнийОбновленныйЭлемент.Дата;
			КонецЕсли;
			Запись.ДатаИзмененияЗаписиРегистра = ТекущаяДатаСеанса();
			ПланОбновления.Записать();
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если ЗаданияУдалены Тогда
		ПараметрыОбновления.ЕстьЗадания = Ложь;
		ПараметрыОбновления.ТочечноеЗадание = Неопределено;
		ПараметрыОбновления.ПоследнийОбновленныйЭлемент = НачальныйЭлемент(ПараметрыОбновления);
		Возврат;
	КонецЕсли;
	
	ОчиститьЗагруженныеЗаписи(ПланОбновления, Выгрузка)
	
КонецПроцедуры

// Для процедуры ПодготовитьПланОбновления.
Процедура ОчиститьЗагруженныеЗаписи(ПланОбновления, Выгрузка)
	
	ПланОбновления.Очистить();
	Для Каждого Строка Из Выгрузка Цикл
		ПланОбновления.Отбор.КлючУникальности.Установить(Строка.КлючУникальности);
		ПланОбновления.Записать();
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ПодготовитьПланОбновления.
Процедура ОбработатьПланОбновленияКэшаРасчетаПрав(Выгрузка, НоваяВерсияДанныхДляКэша = Неопределено)
	
	ВерсияДанныхДляКэша = НоваяВерсияДанныхДляКэшаРасчетаПрав();
	
	Для Каждого Строка Из Выгрузка Цикл
		ПараметрыЗадания = Строка.ПараметрыЗадания.Получить();
		Если ТипЗнч(ПараметрыЗадания) <> Тип("Структура")
		 Или Не ПараметрыЗадания.Свойство("ТочечноеЗадание")
		 Или ТипЗнч(ПараметрыЗадания.ТочечноеЗадание) <> Тип("Структура")
		 Или Не ПараметрыЗадания.ТочечноеЗадание.Свойство("ПоДаннымКэшаРасчетаПрав") Тогда
			Продолжить;
		КонецЕсли;
		ИмяИзмененныхДанных = ПараметрыЗадания.ТочечноеЗадание.ПоДаннымКэшаРасчетаПрав;
		ОбработатьЗаданиеОбновленияКэшаРасчетаПрав(ВерсияДанныхДляКэша,
			ИмяИзмененныхДанных, Строка.КлючУникальности);
	КонецЦикла;
	
	ИмяПараметра = ИмяПараметраВерсииДанныхДляКэшаРасчетаПрав();
	
	Если НоваяВерсияДанныхДляКэша = Неопределено Тогда
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ПараметрыРаботыВерсийРасширений");
		ЭлементБлокировки.УстановитьЗначение("ВерсияРасширений", Справочники.ВерсииРасширений.ПустаяСсылка());
		ЭлементБлокировки.УстановитьЗначение("ИмяПараметра", ИмяПараметра);
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			ТекущееЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
			НовоеЗначение = НоваяВерсияДанныхДляКэша(ТекущееЗначение, ВерсияДанныхДляКэша);
			СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, НовоеЗначение, Истина);
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	Иначе
		ТекущееЗначение = СтандартныеПодсистемыСервер.ПараметрРаботыРасширения(ИмяПараметра, Истина);
		НоваяВерсияДанныхДляКэша = НоваяВерсияДанныхДляКэша(ТекущееЗначение, ВерсияДанныхДляКэша, Ложь);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбработатьПланОбновленияКэшаРасчетаПрав.
Процедура ОбработатьЗаданиеОбновленияКэшаРасчетаПрав(ВерсияДанныхДляКэша, ИмяИзмененныхДанных, КлючУникальности)
	
	Если ТипЗнч(ИмяИзмененныхДанных) <> Тип("Строка")
	 Или Не ВерсияДанныхДляКэша.Свойство(ИмяИзмененныхДанных) Тогда
		
		Для Каждого КлючИЗначение Из ВерсияДанныхДляКэша Цикл
			ВерсияДанныхДляКэша[КлючИЗначение.Ключ] = Строка(Новый УникальныйИдентификатор);
		КонецЦикла;
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(ВерсияДанныхДляКэша[ИмяИзмененныхДанных]) = Тип("Строка") Тогда
		Возврат;
	КонецЕсли;
	
	Если ВерсияДанныхДляКэша[ИмяИзмененныхДанных] = Неопределено Тогда
		ВерсияДанныхДляКэша[ИмяИзмененныхДанных] = Новый ХешированиеДанных(ХешФункция.SHA256);
	КонецЕсли;
	
	ХешированиеДанных = ВерсияДанныхДляКэша[ИмяИзмененныхДанных]; // ХешированиеДанных
	ХешированиеДанных.Добавить(Строка(КлючУникальности));
	
КонецПроцедуры

// Для процедуры ОбработатьПланОбновленияКэшаРасчетаПрав.
Функция НоваяВерсияДанныхДляКэша(ТекущееЗначение, ВерсияДанныхДляКэша, ДляЗаписи = Истина)
	
	НовоеЗначение = НоваяВерсияДанныхДляКэшаРасчетаПрав();
	
	Если ТипЗнч(ТекущееЗначение) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(НовоеЗначение, ТекущееЗначение);
	КонецЕсли;
	
	Для Каждого КлючИЗначение Из ВерсияДанныхДляКэша Цикл
		Если КлючИЗначение.Значение = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Если ТипЗнч(КлючИЗначение.Значение) = Тип("ХешированиеДанных") Тогда
			НовоеЗначение[КлючИЗначение.Ключ] = Base64Строка(КлючИЗначение.Значение.ХешСумма);
		Иначе
			НовоеЗначение[КлючИЗначение.Ключ] = КлючИЗначение.Значение;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого КлючИЗначение Из НовоеЗначение Цикл
		Если КлючИЗначение.Значение <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		НовоеЗначение[КлючИЗначение.Ключ] = ?(ДляЗаписи,
			Строка(Новый УникальныйИдентификатор), "00000000-0000-0000-0000-000000000000");
	КонецЦикла;
	
	Возврат НовоеЗначение;
	
КонецФункции

// Для функции КэшРасчетаПравДляВидаПользователей.
Функция ВерсияДанныхДляКэшаРасчетаПрав()
	
	ИдентификаторСпискаПланирования = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(
		СписокДляПланированияОбновленияКэшаРасчетаПрав());
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Список", ИдентификаторСпискаПланирования);
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	КлючиУникальности.КлючУникальности КАК КлючУникальности,
	|	КлючиУникальности.ПараметрыЗадания КАК ПараметрыЗадания
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаПользователей КАК КлючиУникальности
	|ГДЕ
	|	КлючиУникальности.Список = &Список
	|	И КлючиУникальности.ДляВнешнихПользователей = ЛОЖЬ
	|
	|УПОРЯДОЧИТЬ ПО
	|	КлючиУникальности.Список,
	|	КлючиУникальности.ДляВнешнихПользователей,
	|	КлючиУникальности.КлючУникальности";
	
	Если ТранзакцияАктивна() И ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		ЗаблокироватьРегистрыПланированияОбновленияКлючейДоступаВФайловойИБ();
	КонецЕсли;
	
	Выгрузка = Запрос.Выполнить().Выгрузить();
	
	ВерсияДанныхДляКэша = Новый Структура;
	ОбработатьПланОбновленияКэшаРасчетаПрав(Выгрузка, ВерсияДанныхДляКэша);
	
	Возврат ВерсияДанныхДляКэша;
	
КонецФункции

// Для процедуры ПодготовитьПланОбновления, ЗаписатьПоследнийОбновленныйЭлемент.
// 
// Возвращаемое значение:
//  Структура:
//    * ПоследнийОбновленныйЭлемент - см. НачальныйЭлемент
//    * ТочечноеЗадание - Структура:
//        ** ПоКлючамДоступа - Соответствие
//        ** ПоЗначениямПолей - Соответствие
//        ** ПоЗначениямСГруппами - Соответствие
//
Функция СохраняемыеПараметрыЗадания(ЭтоОбновлениеПрав, ПараметрыОбновления, ПараметрыЗадания = Неопределено,
			ПерезапускОбновления = Ложь, ОчиститьТочечноеЗадание = Ложь)
	
	ТочечноеЗадание = Новый Структура;
	ТочечноеЗадание.Вставить("ПоКлючамДоступа", Новый Соответствие);
	Если Не ЭтоОбновлениеПрав Тогда
		ТочечноеЗадание.Вставить("ПоЗначениямПолей",     Новый Соответствие);
		ТочечноеЗадание.Вставить("ПоЗначениямСГруппами", Новый Соответствие);
	КонецЕсли;
	
	СохраняемыеПараметры = Новый Структура;
	СохраняемыеПараметры.Вставить("ТочечноеЗадание", ТочечноеЗадание);
	СохраняемыеПараметры.Вставить("ПоследнийОбновленныйЭлемент", НачальныйЭлемент(ПараметрыОбновления));
	
	Если ТипЗнч(ПараметрыЗадания) <> Тип("Структура") Тогда
		Возврат СохраняемыеПараметры;
	КонецЕсли;
	
	Если ПараметрыЗадания.Свойство("ПоследнийОбновленныйЭлемент") Тогда
		Порядок = ПорядокВидаКлючаДанных(ПараметрыЗадания.ПоследнийОбновленныйЭлемент.ВидКлючаДанных);
		Если Порядок <> Неопределено Тогда
			ЗаполнитьЗначенияСвойств(СохраняемыеПараметры.ПоследнийОбновленныйЭлемент,
				ПараметрыЗадания.ПоследнийОбновленныйЭлемент);
		КонецЕсли;
	КонецЕсли;
	
	Если ОчиститьТочечноеЗадание Или Не ПараметрыЗадания.Свойство("ТочечноеЗадание") Тогда
		Возврат СохраняемыеПараметры;
	КонецЕсли;
	
	ТекущееТочечноеЗадание = ПараметрыЗадания.ТочечноеЗадание;
	Если ТипЗнч(ТекущееТочечноеЗадание) <> Тип("Структура") Тогда
		ПерезапускОбновления = Истина;
		Возврат СохраняемыеПараметры;
	КонецЕсли;
	
	Для Каждого ВариантЗадания Из ТочечноеЗадание Цикл
		Если Не ТекущееТочечноеЗадание.Свойство(ВариантЗадания.Ключ)
		 Или ТипЗнч(ТекущееТочечноеЗадание[ВариантЗадания.Ключ]) <> Тип("Соответствие") Тогда
			ПерезапускОбновления = Истина;
			Продолжить;
		КонецЕсли;
		ТочечноеЗадание[ВариантЗадания.Ключ] = ТекущееТочечноеЗадание[ВариантЗадания.Ключ];
	КонецЦикла;
	
	Возврат СохраняемыеПараметры;
	
КонецФункции

// Для процедуры ПодготовитьПланОбновления.
Процедура ДобавитьВедущийОбъектКТочечномуЗаданию(ТочечноеЗадание, СохраняемоеТочечноеЗадание,
			ПерезапускОбновления)
	
	Если ТипЗнч(ТочечноеЗадание) <> Тип("Структура") Тогда
		ПерезапускОбновления = Истина;
		Возврат;
	КонецЕсли;
	МаксимальноеКоличество = МаксимальноеКоличествоКомбинацийЗначенийВедущихПолейПриВычисленииСоставаИзмененных();
	
	Для Каждого ВариантЗадания Из ТочечноеЗадание Цикл
		Если Не СохраняемоеТочечноеЗадание.Свойство(ВариантЗадания.Ключ) Тогда
			ПерезапускОбновления = Истина;
			Продолжить;
		КонецЕсли;
		Если ВариантЗадания.Ключ <> "ПоЗначениямПолей" Тогда
			Ссылки = ?(ТипЗнч(ВариантЗадания.Значение) = Тип("Массив"), ВариантЗадания.Значение,
				ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ВариантЗадания.Значение));
			СохраняемыеСсылки = СохраняемоеТочечноеЗадание[ВариантЗадания.Ключ];
			Для Каждого Ссылка Из Ссылки Цикл
				СохраняемыеСсылки.Вставить(Ссылка, Истина);
				Если СохраняемыеСсылки.Количество() >= МаксимальноеКоличество Тогда
					ПерезапускОбновления = Истина;
					Продолжить;
				КонецЕсли;
			КонецЦикла;
		Иначе
			Свойства = ВариантЗадания.Значение;
			СохраняемыеТаблицы = СохраняемоеТочечноеЗадание[ВариантЗадания.Ключ];
			
			Если Не Свойства.Свойство("ИзмененнаяТаблица")
			 Или Не Свойства.Свойство("СоставИзменений")
			 Или ТипЗнч(Свойства.СоставИзменений) <> Тип("ТаблицаЗначений")
			 Или ТипЗнч(СохраняемыеТаблицы) <> Тип("Соответствие") Тогда
				
				ПерезапускОбновления = Истина;
				Продолжить;
			КонецЕсли;
			СохраняемаяТаблица = СохраняемыеТаблицы.Получить(Свойства.ИзмененнаяТаблица);
			СоставИзменений = Свойства.СоставИзменений;
			Если СохраняемаяТаблица = Неопределено Тогда
				СохраняемыеТаблицы.Вставить(Свойства.ИзмененнаяТаблица, СоставИзменений);
				
			ИначеЕсли ТипЗнч(СохраняемаяТаблица) <> Тип("ТаблицаЗначений")
			      Или СоставИзменений.Колонки.Количество() <> СохраняемаяТаблица.Колонки.Количество() Тогда
				
				ПерезапускОбновления = Истина;
				Продолжить;
			Иначе
				ИменаКолонок = Новый Массив;
				Для Каждого Колонка Из СохраняемаяТаблица.Колонки Цикл
					Если СоставИзменений.Колонки.Найти(Колонка.Имя) = Неопределено Тогда
						ИменаКолонок = Неопределено;
						Прервать;
					КонецЕсли;
					ИменаКолонок.Добавить(Колонка.Имя);
				КонецЦикла;
				Если ИменаКолонок = Неопределено Тогда
					ПерезапускОбновления = Истина;
					Продолжить;
				КонецЕсли;
				СписокИменКолонок = СтрСоединить(ИменаКолонок, ",");
				Отбор = Новый Структура(СписокИменКолонок);
				Если СохраняемаяТаблица.Индексы.Количество() = 0 Тогда
					СохраняемаяТаблица.Индексы.Добавить(СписокИменКолонок);
				КонецЕсли;
				Для Каждого Строка Из СоставИзменений Цикл
					ЗаполнитьЗначенияСвойств(Отбор, Строка);
					Если СохраняемаяТаблица.НайтиСтроки(Отбор).Количество() = 0 Тогда
						Если СохраняемаяТаблица.Количество() >= МаксимальноеКоличество Тогда
							ПерезапускОбновления = Истина;
							Прервать;
						КонецЕсли;
						ЗаполнитьЗначенияСвойств(СохраняемаяТаблица.Добавить(), Строка);
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ПодготовитьПланОбновления.
Функция ЕстьСвойстваКлючаДанных(ПараметрыЗадания)
	
	Если ТипЗнч(ПараметрыЗадания) <> Тип("Структура")
	 Или Не ПараметрыЗадания.Свойство("ВидКлючаДанных")
	 Или Не ПараметрыЗадания.Свойство("ПорядокВидаКлючаДанных") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Порядок = ПорядокВидаКлючаДанных(ПараметрыЗадания.ВидКлючаДанных);
	
	Возврат Порядок <> Неопределено И Порядок = ПараметрыЗадания.ПорядокВидаКлючаДанных;
	
КонецФункции

// Для процедур ВыполнитьОбновлениеДоступаСписка, ОбновитьПорциюЭлементов,
// УстановитьТекстЗапросаИПараметрыПоследнегоОбновленногоЭлементаДанных, ОбновитьНаборыГруппДоступа и
// функций ЭлементыДляОбновления, ПоследнийЭлемент, КлючДанных, НаборыГруппДоступаДляОбновления.
//
Функция ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления)
	
	Порядок = ПорядокВидаКлючаДанных(ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных);
	
	Возврат Порядок >= ПорядокВидаКлючаДанных("УстаревшиеЭлементы")
	      И Порядок < ПорядокВидаКлючаДанных("НетДанных");
	
КонецФункции

// Для функций СохраняемыеПараметрыЗадания, ЕстьСвойстваКлючаДанных и
// процедур ОбновитьСвойствоЭтоОбработкаУстаревшихЭлементов, УстановитьВидКлючаДанных.
//
Функция ПорядокВидаКлючаДанных(ВидКлючаДанных)
	
	ВидыКлючейДанных = Новый Соответствие;
	ВидыКлючейДанных.Вставить("ЭлементыСУстаревшимиКлючами", 0);
	ВидыКлючейДанных.Вставить("ЭлементыБезКлючейПоЗначениямПолей", 1);
	ВидыКлючейДанных.Вставить("ЭлементыБезКлючейПоПериоду", 1);
	
	ВидыКлючейДанных.Вставить("ЭлементыСУстаревшимиПравами", 0);
	
	ВидыКлючейДанных.Вставить("НовыеНаборыИзОдногоПользователя", 0);
	ВидыКлючейДанных.Вставить("НаборыГруппДоступаНазначенныеПользователям", 1);
	ВидыКлючейДанных.Вставить("НаборыГруппПользователейНазначенныеПользователям", 2);
	ВидыКлючейДанных.Вставить("НовыеНаборыГруппСУстаревшимиПравами", 3);
	ВидыКлючейДанных.Вставить("НаборыГруппРазрешенныеПользователям", 4);
	ВидыКлючейДанных.Вставить("НаборыГруппСУстаревшимиПравами", 5);
	
	ВидыКлючейДанных.Вставить("УстаревшиеЭлементы", 10);
	ВидыКлючейДанных.Вставить("УстаревшиеЭлементыОбщегоРегистра", 11);
	
	ВидыКлючейДанных.Вставить("НетДанных", 99);
	
	Возврат ВидыКлючейДанных.Получить(ВидКлючаДанных);
	
КонецФункции

// Для процедур ЗапланироватьОбновлениеДоступа, ЗапланироватьОбновлениеНаборовГруппДоступа,
// УточнитьПустойПоследнийЭлемент и функции НачальныйЭлемент.
//
Процедура УстановитьВидКлючаДанных(Элемент, ВидКлючаДанных)
	
	Порядок = ПорядокВидаКлючаДанных(ВидКлючаДанных);
	Если Порядок = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректный имя вида порядка ключа данных ""%1""'"), ВидКлючаДанных);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	Элемент.Вставить("ВидКлючаДанных", ВидКлючаДанных);
	Элемент.Вставить("ПорядокВидаКлючаДанных", Порядок);
	
КонецПроцедуры

// Для процедуры ПодготовитьПланОбновления.
Функция ПодготовленноеТочечноеЗадание(ЭтоОбновлениеПрав, СохраняемоеТочечноеЗадание, ПерезапускОбновления)
	
	ТочечноеЗадание = Новый Структура;
	Если ЭтоОбновлениеПрав Тогда
		ТочечноеЗадание.Вставить("ПоКлючамДоступа", Новый Массив);
	Иначе
		ТочечноеЗадание.Вставить("ПоЗначениямПолей",     Новый Соответствие);
		ТочечноеЗадание.Вставить("ПоКлючамДоступа",      Новый ТаблицаЗначений);
		ТочечноеЗадание.Вставить("ПоЗначениямСГруппами", Новый ТаблицаЗначений);
	КонецЕсли;
	
	ЭтоПустоеЗадание = Истина;
	МаксимальноеКоличество = МаксимальноеКоличествоКомбинацийЗначенийВедущихПолейПриВычисленииСоставаИзмененных();
	ТипыВедущихОбъектов = УправлениеДоступомСлужебныйПовтИсп.ТиповСсылокВедущихОбъектов();
	
	Для Каждого ВариантЗадания Из СохраняемоеТочечноеЗадание Цикл
		Если ВариантЗадания.Ключ = "ПоЗначениямПолей" Тогда
			Если ЗначениеЗаполнено(ВариантЗадания.Значение) Тогда
				ЭтоПустоеЗадание = Ложь;
				ТочечноеЗадание.ПоЗначениямПолей = ВариантЗадания.Значение;
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		УдаляемыеСсылки = Новый Массив;
		ОписаниеСсылок = ВариантЗадания.Значение;
		Если ЭтоОбновлениеПрав Тогда
			Ссылки = ТочечноеЗадание[ВариантЗадания.Ключ];
		Иначе
			Ссылки = Новый Массив;
			Типы = Новый Соответствие;
			Таблица = ТочечноеЗадание[ВариантЗадания.Ключ]; // ТаблицаЗначений
		КонецЕсли;
		Для Каждого ОписаниеСсылки Из ОписаниеСсылок Цикл
			Ссылка = ОписаниеСсылки.Ключ;
			Тип = ТипЗнч(Ссылка);
			Если ТипыВедущихОбъектов.Получить(Тип) = Неопределено Тогда
				ПерезапускОбновления = Истина;
				УдаляемыеСсылки.Добавить(Ссылка);
				Продолжить;
			КонецЕсли;
			Ссылки.Добавить(Ссылка);
			Если Не ЭтоОбновлениеПрав Тогда
				Типы.Вставить(Тип, Истина);
				Таблица.Добавить();
			КонецЕсли;
			Если Ссылки.Количество() >= МаксимальноеКоличество Тогда
				ПерезапускОбновления = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Для Каждого Ссылка Из УдаляемыеСсылки Цикл
			ОписаниеСсылок.Удалить(Ссылка);
		КонецЦикла;
		Если Ссылки.Количество() > 0 Тогда
			ЭтоПустоеЗадание = Ложь;
		Иначе
			Продолжить;
		КонецЕсли;
		Если Не ЭтоОбновлениеПрав Тогда
			ТипыКолонки = Новый Массив;
			Для Каждого КлючИЗначение Из Типы Цикл
				ТипыКолонки.Добавить(КлючИЗначение.Ключ);
			КонецЦикла;
			Таблица.Колонки.Добавить("Ссылка", Новый ОписаниеТипов(ТипыКолонки));
			Таблица.ЗагрузитьКолонку(Ссылки, "Ссылка");
		КонецЕсли;
	КонецЦикла;
	
	Если ЭтоПустоеЗадание Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат ТочечноеЗадание;
	
КонецФункции

// Для процедура ВыполнитьОбновлениеДоступаСписка.
Функция КоличествоЭлементовВПорции(ПараметрыОбновления)
	
	Если ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления) Тогда
		Возврат 25;
	ИначеЕсли ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		Возврат КоличествоКлючейДоступаВПорции();
	Иначе
		Возврат КоличествоЭлементовДанныхВПорции();
	КонецЕсли;
	
КонецФункции

// Для процедура ВыполнитьОбновлениеДоступаСписка.
Функция КоличествоЭлементовВЗапросе(ЭтоОбновлениеПрав)
	
	Если ЭтоОбновлениеПрав Тогда
		Возврат КоличествоКлючейДоступаВЗапросе();
	Иначе
		Возврат КоличествоЭлементовДанныхВЗапросе();
	КонецЕсли;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура ОбновитьПорциюЭлементов(Элементы, ПараметрыОбновления, ЭтоТочечноеЗадание = Ложь)
	
	ПараметрыОбновления.Вставить("КоличествоОбработанныхЭлементов", 0);
	
	Если ЭтоТочечноеЗадание Тогда
		Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
			ОбновитьПраваПорцииКлючейДоступаСписка(Элементы, ПараметрыОбновления);
		Иначе
			ОбновитьЭлементыДанныхСпискаСУстаревшимиКлючами(Элементы, ПараметрыОбновления);
		КонецЕсли;
		
	ИначеЕсли ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		Если ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
			ОбработатьУстаревшиеКлючиДоступаСписка(Элементы, ПараметрыОбновления);
		Иначе
			ОбновитьПраваПорцииКлючейДоступаСписка(Элементы, ПараметрыОбновления);
		КонецЕсли;
		
	ИначеЕсли ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления) Тогда
		ОбновитьНаборыГруппДоступа(Элементы, ПараметрыОбновления);
		
	ИначеЕсли ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
		УдалитьУстаревшиеЭлементыДанныхСписка(Элементы, ПараметрыОбновления);
	Иначе
		ОбновитьЭлементыДанныхСпискаСУстаревшимиКлючами(Элементы, ПараметрыОбновления);
	КонецЕсли;
	
	Если Не ЭтоТочечноеЗадание И Элементы.Количество() > 0 Тогда
		ПараметрыОбновления.Вставить("НовыйПоследнийОбновленныйЭлемент",
			ПоследнийЭлемент(Элементы, ПараметрыОбновления, Истина));
	КонецЕсли;
	
	Если Элементы.Количество() = ПараметрыОбновления.КоличествоОбработанныхЭлементов Тогда
		Элементы = Неопределено;
	Иначе
		Для Счетчик = 1 По ПараметрыОбновления.КоличествоОбработанныхЭлементов Цикл
			Элементы.Удалить(0);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ОбновитьНаборыГруппДоступа, УдалитьУстаревшиеЭлементыДанныхСписка,
// ЗаписатьКлючиДоступаОбъектов, ЗаписатьКлючиДоступаРегистров,
// ОбновитьПраваПорцииКлючейДоступаСписка, УдалитьПорциюКлючейДоступаСписка,
// УдалитьТекущуюПорциюКлючейДоступаСписка.
//
Функция ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, КоличествоОбработанныхНаШаге = 1)
	
	Если Не ПараметрыОбновления.Свойство("КоличествоОбработанныхЭлементов")
	 Или Не ПараметрыОбновления.Свойство("ГраницаВремениОбработки") Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ПараметрыОбновления.КоличествоОбработанныхЭлементов =
		ПараметрыОбновления.КоличествоОбработанныхЭлементов + КоличествоОбработанныхНаШаге;
	
	Если ТекущаяУниверсальнаяДатаВМиллисекундах() > ПараметрыОбновления.ГраницаВремениОбработки Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Для процедур ВыполнитьОбновлениеДоступаСписка, ПроверитьЗавершитьОбновлениеПоПорциям.
Процедура ЗаписатьПоследнийОбновленныйЭлемент(ОбщиеПараметрыОбновления, ПоследнийОбновленныйЭлемент)
	
	ЭтоОбновлениеПрав       = ОбщиеПараметрыОбновления.ЭтоОбновлениеПрав;
	ДляВнешнихПользователей = ОбщиеПараметрыОбновления.ДляВнешнихПользователей;
	ИдентификаторСписка     = ОбщиеПараметрыОбновления.ИдентификаторСписка;
	
	МенеджерРегистра = ?(ЭтоОбновлениеПрав, РегистрыСведений.ОбновлениеКлючейДоступаПользователей,
		РегистрыСведений.ОбновлениеКлючейДоступаКДанным);
	
	ПланОбновления = СлужебныйНаборЗаписей(МенеджерРегистра);
	ПланОбновления.Отбор.Список.Установить(ИдентификаторСписка);
	ПланОбновления.Отбор.ДляВнешнихПользователей.Установить(ДляВнешнихПользователей);
	ПланОбновления.Отбор.КлючУникальности.Установить(
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	ПолноеИмяРегистра = ?(ЭтоОбновлениеПрав, "РегистрСведений.ОбновлениеКлючейДоступаПользователей",
		"РегистрСведений.ОбновлениеКлючейДоступаКДанным");
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяРегистра);
	ЭлементБлокировки.УстановитьЗначение("Список", ИдентификаторСписка);
	ЭлементБлокировки.УстановитьЗначение("ДляВнешнихПользователей", ДляВнешнихПользователей);
	ЭлементБлокировки.УстановитьЗначение("КлючУникальности",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		ПланОбновления.Прочитать();
		Если ПланОбновления.Количество() > 0 Тогда
			Если ПоследнийОбновленныйЭлемент.КлючДанных = Null Тогда
				ПланОбновления.Очистить();
			Иначе
				Запись = ПланОбновления[0];
				Если ПоследнийОбновленныйЭлемент.Свойство("ОчиститьТочечноеЗадание") Тогда
					Запись.ТочечноеЗадание = Ложь;
				КонецЕсли;
				
				ТекущиеПараметрыЗадания = Запись.ПараметрыЗадания.Получить();
				ПараметрыЗадания = СохраняемыеПараметрыЗадания(ЭтоОбновлениеПрав, ОбщиеПараметрыОбновления,
					ТекущиеПараметрыЗадания, , ПоследнийОбновленныйЭлемент.Свойство("ОчиститьТочечноеЗадание"));
				
				ЗаполнитьЗначенияСвойств(ПараметрыЗадания.ПоследнийОбновленныйЭлемент, ПоследнийОбновленныйЭлемент);
				
				Если Не ЭтоОбновлениеПрав И ПоследнийОбновленныйЭлемент.Свойство("Дата") Тогда
					Запись.ДатаПоследнегоОбновленногоЭлемента = ПоследнийОбновленныйЭлемент.Дата;
				КонецЕсли;
				
				Запись.ПараметрыЗадания = Новый ХранилищеЗначения(ПараметрыЗадания);
				Запись.РазмерЗадания = ?(ЭтоОбработкаУстаревшихЭлементов(
					Новый Структура("ПоследнийОбновленныйЭлемент", ПоследнийОбновленныйЭлемент)), 2, 3);
				
				Запись.ДатаИзмененияЗаписиРегистра = ТекущаяДатаСеанса();
				ОбщиеПараметрыОбновления.ОбработкаЗавершена = Ложь;
			КонецЕсли;
			ПланОбновления.Записать();
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Функция ЭлементыДляОбновления(ПараметрыОбновления, КоличествоВЗапросе, ВыбраныВсеЭлементы)
	
	Если ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления) Тогда
		Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
			ВыбраныВсеЭлементы = Истина;
			Возврат Неопределено;
		КонецЕсли;
		Элементы = НаборыГруппДоступаДляОбновления(ПараметрыОбновления, КоличествоВЗапросе);
	Иначе
		Запрос = Новый Запрос;
		Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
			Запрос.УстановитьПараметр("Список", ПараметрыОбновления.ИдентификаторСписка);
			Запрос.УстановитьПараметр("ДляВнешнихПользователей", ПараметрыОбновления.ДляВнешнихПользователей);
			
			ПоследнийКлючДоступа = ПараметрыОбновления.ПоследнийОбновленныйЭлемент.КлючДанных;
			Если ТипЗнч(ПоследнийКлючДоступа) <> Тип("СправочникСсылка.КлючиДоступа") Тогда
				ПоследнийКлючДоступа = Неопределено;
			КонецЕсли;
			Запрос.УстановитьПараметр("ПоследнийКлючДоступа", ПоследнийКлючДоступа);
			
			Если ПараметрыОбновления.БезЗаписиКлючейДоступа Тогда
				// Для режима ЭтоОчисткаВыбранныхКлючей в процедуре ОбработатьУстаревшиеКлючиДоступаСписка.
				// Переключение на шаг УстаревшиеЭлементы выполняется в процедуре УточнитьПоследнийОбновленныйЭлемент.
				Запрос.Текст =
				"ВЫБРАТЬ ПЕРВЫЕ 995
				|	КлючиДоступа.Ссылка КАК Ссылка
				|ИЗ
				|	Справочник.КлючиДоступа КАК КлючиДоступа
				|ГДЕ
				|	КлючиДоступа.Список = &Список
				|	И КлючиДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
				|	И КлючиДоступа.Ссылка > &ПоследнийКлючДоступа
				|	И &УточнениеПланаЗапроса
				|
				|УПОРЯДОЧИТЬ ПО
				|	КлючиДоступа.Ссылка";
				
			ИначеЕсли ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
				Если ПараметрыОбновления.СЗаписьюКлючаДоступаДляЗависимыхСписковБезКлючей Тогда
					// Для режима ЭтоОчисткаВыбранныхКлючей в процедуре ОбработатьУстаревшиеКлючиДоступаСписка.
					Запрос.Текст =
					"ВЫБРАТЬ ПЕРВЫЕ 995
					|	КлючиДоступа.Ссылка КАК Ссылка
					|ИЗ
					|	Справочник.КлючиДоступа КАК КлючиДоступа
					|ГДЕ
					|	КлючиДоступа.Список = &Список
					|	И КлючиДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
					|	И КлючиДоступа.СоставПолей > 0
					|	И КлючиДоступа.Ссылка > &ПоследнийКлючДоступа
					|	И &УточнениеПланаЗапроса
					|
					|УПОРЯДОЧИТЬ ПО
					|	КлючиДоступа.Ссылка";
				Иначе
					Запрос.УстановитьПараметр("ДатаУстаревания", ДатаУстаревания());
					Запрос.Текст = ПараметрыОбновления.ТекстЗапросаУстаревшихКлючейДоступа;
				КонецЕсли;
			Иначе
				Запрос.Текст = ПараметрыОбновления.ТекстЗапросаКлючейДоступаДляОбновленияПрав;
			КонецЕсли;
			
		ИначеЕсли ПараметрыОбновления.ЭтоСсылочныйТип
		        И ПараметрыОбновления.ДляВнешнихПользователей
		        И ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления)
		        И (Не ПараметрыОбновления.БезЗаписиКлючейДоступа
		           Или ПараметрыОбновления.БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей) Тогда
			
			ВыбраныВсеЭлементы = Истина;
			Возврат Неопределено;
		Иначе
			УстановитьТекстЗапросаИПараметрыПоследнегоОбновленногоЭлементаДанных(Запрос, ПараметрыОбновления);
		КонецЕсли;
		
		КоличествоВЗапросе = ?(КоличествоВЗапросе < 1000, 1000, ?(КоличествоВЗапросе > 10000, 10000, КоличествоВЗапросе));
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "995", Формат(КоличествоВЗапросе, "ЧГ="));
		УстановитьУточнениеПланаЗапроса(Запрос.Текст);
		
		Элементы = Запрос.Выполнить().Выгрузить();
	КонецЕсли;
	
	Если Элементы.Количество() = 0 Тогда
		ВыбраныВсеЭлементы = Истина;
		Возврат Неопределено;
	КонецЕсли;
	
	ВыбраныВсеЭлементы = Элементы.Количество() < КоличествоВЗапросе;
	
	Возврат Элементы;
	
КонецФункции

// Для процедур выполнения запросов.
Процедура УстановитьУточнениеПланаЗапроса(ТекстЗапроса, УникальныйПлан = Ложь)
	
	Если УправлениеДоступомСлужебныйПовтИсп.ТребуетсяУточнениеПланаЗапроса() Тогда
		Если УникальныйПлан Тогда
			ТекущаяДатаСеанса = ТекущаяДатаСеанса();
			ВсегоМинут   = Цел((ТекущаяДатаСеанса - '00010101') / 60);
			ОстатокМинут = ВсегоМинут - Цел(ВсегоМинут / 2048) * 2048;
			УточнениеПланаЗапроса = УправлениеДоступомСлужебныйПовтИсп.УточнениеПланаЗапроса(ОстатокМинут, 11);
		Иначе
			ТочноеВремя = ТекущаяУниверсальнаяДатаВМиллисекундах();
			ВсегоДесятыхСекунд = Цел(ТочноеВремя / 100);
			ОстатокДесятыхСекунд = ВсегоДесятыхСекунд - Цел(ВсегоДесятыхСекунд / 1048576) * 1048576;
			УточнениеПланаЗапроса = УправлениеДоступомСлужебныйПовтИсп.УточнениеПланаЗапроса(ОстатокДесятыхСекунд, 20);
		КонецЕсли;
	Иначе
		УточнениеПланаЗапроса = "ИСТИНА";
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УточнениеПланаЗапроса", УточнениеПланаЗапроса);
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Функция ЭлементыТочечногоЗаданияДляОбновления(ПараметрыОбновления, КоличествоВЗапросе, ВыбраныВсеЭлементы)
	
	Если ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления)
	 Или ПараметрыОбновления.БезЗаписиКлючейДоступа
	 Или ПараметрыОбновления.СЗаписьюКлючаДоступаДляЗависимыхСписковБезКлючей Тогда
		
		Возврат Неопределено;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		Запрос.Текст = ПараметрыОбновления.ТекстЗапросаКлючейПоВедущимКлючамДляОбновленияПрав;
		Если Не ЗначениеЗаполнено(Запрос.Текст) Тогда
			Возврат Неопределено;
		КонецЕсли;
		Запрос.УстановитьПараметр("Список", ПараметрыОбновления.ИдентификаторСписка);
		Запрос.УстановитьПараметр("ВедущиеКлючиДоступа", ПараметрыОбновления.ТочечноеЗадание.ПоКлючамДоступа);
	Иначе
		Если Не ПараметрыОбновления.ЭтоСсылочныйТип Тогда
			Запрос.УстановитьПараметр("ИдентификаторРегистра", ПараметрыОбновления.ИдентификаторСписка);
		КонецЕсли;
		ОписаниеЗапросов = ПараметрыОбновления.ОписаниеЗапросовУстаревшихКлючейДоступаПоВедущимОбъектам;
		ЗапросыПакета = Новый Массив;
		ЗапросыДанных = Новый Массив;
		Если ЗначениеЗаполнено(ПараметрыОбновления.ТочечноеЗадание.ПоЗначениямПолей) Тогда
			ОписаниеЗапросовПоЗначениямПолей = ОписаниеЗапросов.Получить("ПоЗначениямПолей");
			Если ОписаниеЗапросовПоЗначениямПолей = Неопределено Тогда
				Возврат Неопределено;
			КонецЕсли;
			Для Каждого ОписаниеИзменений Из ПараметрыОбновления.ТочечноеЗадание.ПоЗначениямПолей Цикл
				ОписаниеЗапроса = ОписаниеЗапросовПоЗначениямПолей.Получить(ОписаниеИзменений.Ключ);
				Если ОписаниеЗапроса = Неопределено Тогда
					Возврат Неопределено;
				КонецЕсли;
				СоставИзменений = ОписаниеИзменений.Значение; // ТаблицаЗначений
				Если ОписаниеЗапроса.ТипыПолей.Количество() <> СоставИзменений.Колонки.Количество() Тогда
					Возврат Неопределено;
				КонецЕсли;
				Для Каждого Колонка Из СоставИзменений.Колонки Цикл
					ТипыПоля = ОписаниеЗапроса.ТипыПолей.Получить(Колонка.Имя);
					Если ТипЗнч(ТипыПоля) <> Тип("ХранилищеЗначения") Тогда
						Возврат Неопределено;
					КонецЕсли;
					ТипКолонки = Новый ОписаниеТипов(Колонка.ТипЗначения,, "Null");
					Если ТипыПоля.Получить() <> ТипКолонки
					 Или СтрокаДанныхДляХеширования(Колонка.ТипЗначения)
					       <> СтрокаДанныхДляХеширования(Новый ОписаниеТипов(Колонка.ТипЗначения.Типы())) Тогда
						Возврат Неопределено;
					КонецЕсли;
				КонецЦикла;
				ИмяВременнойТаблицыИПараметра = СтрЗаменить(ОписаниеИзменений.Ключ, ".", "_");
				Запрос.УстановитьПараметр(ИмяВременнойТаблицыИПараметра, СоставИзменений);
				ЗапросыПакета.Добавить(ОписаниеЗапроса.ТекстЗапросаПараметров);
				Для Каждого ТекстЗапросаДанных Из ОписаниеЗапроса.ТекстыЗапросовДанных Цикл
					ЗапросыДанных.Добавить(ТекстЗапросаДанных);
				КонецЦикла;
			КонецЦикла;
		КонецЕсли;
		Если Не ДобавитьЗапросыТочечногоЗадания("ПоКлючамДоступа",
		            Запрос, ЗапросыПакета, ЗапросыДанных, ПараметрыОбновления)
		 Или Не ДобавитьЗапросыТочечногоЗадания("ПоЗначениямСГруппами",
		            Запрос, ЗапросыПакета, ЗапросыДанных, ПараметрыОбновления)
		 Или ЗапросыДанных.Количество() = 0 Тогда
			Возврат Неопределено;
		КонецЕсли;
		Если ЗапросыДанных.Количество() = 1 Тогда
			ЗапросыПакета.Добавить(СтрЗаменить(ЗапросыДанных[0],
			"
			|ИЗ
			|	", // @query-part-1
			"
			|ПОМЕСТИТЬ ТекущийСписокПоВедущимОбъектам
			|ИЗ
			|	")); // @query-part-1
		Иначе
			ТекстыЗапросов = СтрСоединить(ЗапросыДанных,
			"
			|
			|ОБЪЕДИНИТЬ ВСЕ
			|
			|"); // @query-part-1
			ЗапросыПакета.Добавить(СтрЗаменить(ОписаниеЗапросов.Получить("ТекстЗапросаОберткиВыбораДанных"),
				"#ЗапросыВыбораДанных", "(" + ТекстСОтступом(ТекстыЗапросов, "	") + ")"));
		КонецЕсли;
		ЗапросыПакета.Добавить(ОписаниеЗапросов.Получить("ТекстЗапросаТочечнойПроверки"));
		Запрос.Текст = СтрСоединить(ЗапросыПакета, ОбщегоНазначения.РазделительПакетаЗапросов());
	КонецЕсли;
	
	КоличествоВЗапросе = ?(КоличествоВЗапросе < 1000, 1000, ?(КоличествоВЗапросе > 4000, 4000, КоличествоВЗапросе));
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "995", Формат(КоличествоВЗапросе, "ЧГ="));
	УстановитьУточнениеПланаЗапроса(Запрос.Текст);
	
	Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		Элементы = Запрос.Выполнить().Выгрузить();
		КоличествоВыбранных = Элементы.Количество();
	Иначе
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
		КоличествоВыбранных = РезультатыЗапроса[ЗапросыПакета.Количество() - 2].Выгрузить()[0].Количество;
		Элементы = РезультатыЗапроса[ЗапросыПакета.Количество() - 1].Выгрузить();
	КонецЕсли;
	
	ВыбраныВсеЭлементы = КоличествоВыбранных < КоличествоВЗапросе;
	
	Если Элементы.Количество() = 0 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат Элементы;
	
КонецФункции

// Для функции ЭлементыТочечногоЗаданияДляОбновления.
Функция ДобавитьЗапросыТочечногоЗадания(ВидЗадания, Запрос, ЗапросыПакета, ЗапросыДанных, ПараметрыОбновления)
	
	Данные = ТаблицаТочечногоЗадания();
	Данные = ПараметрыОбновления.ТочечноеЗадание[ВидЗадания]; // см. ТаблицаТочечногоЗадания
	Если Не ЗначениеЗаполнено(Данные) Тогда
		Возврат Истина;
	КонецЕсли;
	
	ОписаниеЗапросов =
		ПараметрыОбновления.ОписаниеЗапросовУстаревшихКлючейДоступаПоВедущимОбъектам.Получить(ВидЗадания);
	
	Если ОписаниеЗапросов = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ТипКолонки = Данные.Колонки.Ссылка.ТипЗначения;
	НедостающиеТипы = Новый ОписаниеТипов(ТипКолонки, , ОписаниеЗапросов.ТипСсылки.Получить().Типы());
	Если НедостающиеТипы.Типы().Количество() > 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Запрос.УстановитьПараметр(ВидЗадания, Данные);
	ЗапросыПакета.Добавить(ОписаниеЗапросов.ТекстЗапросаПараметров);
	
	КлючиЗапросов = Новый Соответствие;
	Для Каждого Тип Из ТипКолонки.Типы() Цикл
		ТекущиеКлючи = ОписаниеЗапросов.КлючиЗапросовПоТипам.Получить(Тип);
		Если ТекущиеКлючи = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
		Для Каждого Ключ Из ТекущиеКлючи Цикл
			КлючиЗапросов.Вставить(Ключ, Истина);
		КонецЦикла;
	КонецЦикла;
	
	Для Каждого КлючИЗначение Из КлючиЗапросов Цикл
		ТекстЗапроса = ОписаниеЗапросов.ТекстыЗапросовПоКлючам.Получить(КлючИЗначение.Ключ);
		Если ТекстЗапроса = Неопределено Тогда
			Возврат Ложь;
		КонецЕсли;
		ЗапросыДанных.Добавить(ТекстЗапроса);
	КонецЦикла;
	
	Возврат Истина;
	
КонецФункции

// Возвращаемое значение:
//   ТаблицаЗначений:
//     * Ссылка - ЛюбаяСсылка
//
Функция ТаблицаТочечногоЗадания()
	
	Возврат Неопределено;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура ПерезапуститьОбновлениеПриНезавершенномТочечномОбновлении(ПараметрыОбновления)
	
	ПараметрыОбновления.ПерезапускОбновления = Истина;
	ПараметрыОбновления.ПоследнийОбновленныйЭлемент = НачальныйЭлемент(ПараметрыОбновления);
	
	// Регистрация косвенного планирования обновления доступа.
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа(Ложь);
	ПараметрыПланирования.РазрешенныеКлючиДоступа  =    ПараметрыОбновления.ЭтоОбновлениеПрав;
	ПараметрыПланирования.ДляПользователей         = Не ПараметрыОбновления.ДляВнешнихПользователей;
	ПараметрыПланирования.ДляВнешнихПользователей  =    ПараметрыОбновления.ДляВнешнихПользователей;
	ПараметрыПланирования.ЭтоПродолжениеОбновления = Истина;
	ПараметрыПланирования.Описание = "ПерезапуститьОбновлениеПриНезавершенномТочечномОбновлении";
	
	СпискиПоИдентификаторам = Новый Соответствие;
	СпискиПоИдентификаторам.Вставить(ПараметрыОбновления.ИдентификаторСписка, ПараметрыОбновления.Список);
	
	ЗарегистрироватьПланированиеОбновленияДоступа(СпискиПоИдентификаторам, ПараметрыПланирования);
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Функция НаборПорцийЭлементов(ПараметрыОбновления, Элементы, ВыбраныВсеЭлементы, РазмерПорции = Неопределено)
	
	НаборПорций = Новый Массив;
	
	Если Элементы = Неопределено Тогда
		ПорцияИзНабора = ПорцияИзНабора();
		ПорцияИзНабора.Вставить("Обработана", Истина);
		ПорцияИзНабора.Вставить("Обрабатывается", Ложь);
		ПорцияИзНабора.Вставить("Элементы", Новый ХранилищеЗначения(Новый ТаблицаЗначений));
		ПорцияИзНабора.Вставить("ДатаПоследнегоЭлементаПорции", '00010101');
		ПорцияИзНабора.Вставить("ПоследнийЭлементПорции", НачальныйЭлемент(ПараметрыОбновления,, Истина));
		ПорцияИзНабора.Вставить("НовыйПоследнийЭлементПорции");
		НаборПорций.Добавить(ПорцияИзНабора);
		ПорцияИзНабора.ПоследнийЭлементПорции.КлючДанных = Null;
		Если ПорцияИзНабора.ПоследнийЭлементПорции.Свойство("Дата") Тогда
			ПорцияИзНабора.ПоследнийЭлементПорции.Дата = '00010101';
		КонецЕсли;
		УстановитьПустойПоследнийЭлемент(ПорцияИзНабора.НовыйПоследнийЭлементПорции,
			ПараметрыОбновления, '00010101');
		Возврат НаборПорций;
	КонецЕсли;
	
	Если РазмерПорции = Неопределено Тогда
		РазмерПорции = ПараметрыОбновления.КоличествоОбработанныхЭлементов;
		МаксимумНовыхПорций = ПараметрыОбновления.МаксимумПорцийИзИсходной;
		Если Элементы.Количество() / РазмерПорции > МаксимумНовыхПорций Тогда
			РазмерПорции = Элементы.Количество() / МаксимумНовыхПорций;
			Если РазмерПорции <> Цел(РазмерПорции) Тогда
				РазмерПорции = Цел(РазмерПорции) + 1;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	ПараметрыОбновления.Вставить("НаборПорций", НаборПорций);
	Индекс = 0;
	Для Каждого Элемент Из Элементы Цикл
		Если Индекс / РазмерПорции = Цел(Индекс / РазмерПорции) Тогда
			ПорцияИзНабора = Новый Массив;
			НаборПорций.Добавить(ПорцияИзНабора);
		КонецЕсли;
		ПорцияИзНабора.Добавить(Элемент);
		Индекс = Индекс + 1;
	КонецЦикла;
	
	Для Индекс = 0 По НаборПорций.Количество() - 1 Цикл
		ЭлементыПорции = Элементы.Скопировать(НаборПорций[Индекс]);
		ПорцияИзНабора = ПорцияИзНабора();
		ПорцияИзНабора.Вставить("Обработана", Ложь);
		ПорцияИзНабора.Вставить("Обрабатывается", Ложь);
		НаборПорций[Индекс] = ПорцияИзНабора;
		ПорцияИзНабора.Вставить("Элементы", Новый ХранилищеЗначения(ЭлементыПорции));
		ПорцияИзНабора.Вставить("ПоследнийЭлементПорции",
			ПоследнийЭлемент(ЭлементыПорции, ПараметрыОбновления));
		ПорцияИзНабора.Вставить("ДатаПоследнегоЭлементаПорции",
			?(ПорцияИзНабора.ПоследнийЭлементПорции.Свойство("Дата"),
				ПорцияИзНабора.ПоследнийЭлементПорции.Дата, '00010101'));
		ПорцияИзНабора.Вставить("НовыйПоследнийЭлементПорции",
			ПоследнийЭлемент(ЭлементыПорции, ПараметрыОбновления));
	КонецЦикла;
	
	Если ВыбраныВсеЭлементы Тогда
		ПорцияИзНабора.ПоследнийЭлементПорции.КлючДанных = Null;
		УстановитьПустойПоследнийЭлемент(ПорцияИзНабора.НовыйПоследнийЭлементПорции,
			ПараметрыОбновления, '00010101');
	КонецЕсли;
	
	Возврат НаборПорций;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * Обработана                   - Булево
//    * Обрабатывается               - Булево
//    * Элементы                     - ХранилищеЗначения
//    * ДатаПоследнегоЭлементаПорции - Дата
//    * ПоследнийЭлементПорции       - см. НачальныйЭлемент
//    * НовыйПоследнийЭлементПорции  - см. НачальныйЭлемент
//
Функция ПорцияИзНабора()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедур ВыполнитьОбновлениеДоступаСписка, ОбновитьПорциюЭлементов, НаборПорцийЭлементов.
Функция ПоследнийЭлемент(Элементы, ПараметрыОбновления, ПоследнийОбработанный = Ложь)
	
	НомерПоследнего = ?(ПоследнийОбработанный,
		ПараметрыОбновления.КоличествоОбработанныхЭлементов, Элементы.Количество());
	
	ПоследнийЭлемент = Элементы[НомерПоследнего - 1];
	
	ЭлементДанных = НачальныйЭлемент(ПараметрыОбновления, , Истина);
	
	Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		ЭлементДанных.КлючДанных = ПоследнийЭлемент.Ссылка;
		Возврат ЭлементДанных;
	КонецЕсли;
	
	Если ПараметрыОбновления.СписокСДатой
	   И Не ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
		
		ЭлементДанных.Дата = ПоследнийЭлемент.Дата;
		
	ИначеЕсли ПараметрыОбновления.ЭтоСсылочныйТип Тогда
		ЭлементДанных.КлючДанных = ПоследнийЭлемент.ТекущаяСсылка;
	Иначе
		ЭлементДанных.КлючДанных = КлючДанных(ПараметрыОбновления);
		ЗаполнитьЗначенияСвойств(ЭлементДанных.КлючДанных, ПоследнийЭлемент);
		
		Если ПараметрыОбновления.СписокСПериодом
		   И ЭлементДанных.ВидКлючаДанных = "ЭлементыБезКлючейПоПериоду" Тогда
			
			ЭлементДанных.Дата = ПоследнийЭлемент.Период;
		КонецЕсли;
	КонецЕсли;
	
	Возврат ЭлементДанных;
	
КонецФункции

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура УточнитьПоследнийОбновленныйЭлемент(ПараметрыОбновления)
	
	Если ПараметрыОбновления.Список = "Справочник.НаборыГруппДоступа"
	 Или ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "НетДанных" Тогда
		Возврат;
	КонецЕсли;
	
	Если ПараметрыОбновления.БезЗаписиКлючейДоступа Тогда
		Если Не ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ОбработатьУстаревшиеЭлементы Тогда
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент =
				НачальныйЭлемент(ПараметрыОбновления, "НетДанных");
			
		ИначеЕсли Не ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
			ПараметрыОбновления.ПоследнийОбновленныйЭлемент =
				НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементы");
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ВыполнитьОбновлениеДоступаСписка, НаборПорцийЭлементов.
Процедура УстановитьПустойПоследнийЭлемент(Элемент, ПараметрыОбновления, ДатаЭлемента = '00010101')
	
	ДатаЭлемента = '00010101';
	Элемент = НачальныйЭлемент(ПараметрыОбновления, , Истина);
	Элемент.КлючДанных = Null;
	
	// Уточнение нового последнего элемента.
	Если ЭтоСправочникНаборыГруппДоступа(ПараметрыОбновления) Тогда
		Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
			Возврат;
		КонецЕсли;
		НовыйВидКлючаДанных = "";
		Если Элемент.ВидКлючаДанных = "НовыеНаборыИзОдногоПользователя" Тогда
			НовыйВидКлючаДанных = "НаборыГруппДоступаНазначенныеПользователям";
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НаборыГруппДоступаНазначенныеПользователям" Тогда
			НовыйВидКлючаДанных = "НаборыГруппПользователейНазначенныеПользователям";
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НаборыГруппПользователейНазначенныеПользователям" Тогда
			НовыйВидКлючаДанных = "НовыеНаборыГруппСУстаревшимиПравами";
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НовыеНаборыГруппСУстаревшимиПравами" Тогда
			НовыйВидКлючаДанных = "НаборыГруппРазрешенныеПользователям";
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НаборыГруппРазрешенныеПользователям"
		        И Элемент.ОбработатьНаборыГруппСУстаревшимиПравами Тогда
			
			НовыйВидКлючаДанных = "НаборыГруппСУстаревшимиПравами";
			
		ИначеЕсли Элемент.ОбработатьУстаревшиеЭлементы
		        И (    Элемент.ВидКлючаДанных = "НаборыГруппРазрешенныеПользователям"
		           Или Элемент.ВидКлючаДанных = "НаборыГруппСУстаревшимиПравами") Тогда
			
			НовыйВидКлючаДанных = "УстаревшиеЭлементы";
		КонецЕсли;
		Если ЗначениеЗаполнено(НовыйВидКлючаДанных) Тогда
			Элемент = НачальныйЭлемент(ПараметрыОбновления, НовыйВидКлючаДанных);
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если ПараметрыОбновления.ЭтоОбновлениеПрав Тогда
		Если Элемент.ВидКлючаДанных = "ЭлементыСУстаревшимиПравами"
		   И Элемент.ОбработатьУстаревшиеЭлементы Тогда
			
			Элемент = НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементы");
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если Элемент.ВидКлючаДанных = "УстаревшиеЭлементыОбщегоРегистра" Тогда
		Возврат;
	КонецЕсли;
	
	Если Элемент.ВидКлючаДанных = "УстаревшиеЭлементы" Тогда
		Если Не ПараметрыОбновления.ЭтоСсылочныйТип
		   И ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей) Тогда
		
			Элемент = НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементыОбщегоРегистра");
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если ПараметрыОбновления.СписокСДатой Тогда
		УстановитьЭлементСледующегоПериода(ПараметрыОбновления, Элемент, ДатаЭлемента);
		Возврат;
	КонецЕсли;
	
	Если ПараметрыОбновления.ЭтоСсылочныйТип Тогда
		Если Элемент.ВидКлючаДанных = "ЭлементыСУстаревшимиКлючами"
		   И Элемент.ОбработатьУстаревшиеЭлементы
		   И (ПараметрыОбновления.БезЗаписиКлючейДоступа
		      Или Не ПараметрыОбновления.ДляВнешнихПользователей) Тогда
			
			Элемент = НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементы");
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если ПараметрыОбновления.БезЗаписиКлючейДоступа Тогда
		Если Элемент.ВидКлючаДанных = "ЭлементыСУстаревшимиКлючами"
		   И Элемент.ОбработатьУстаревшиеЭлементы Тогда
			
			Элемент = НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементы");
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если Элемент.ВидКлючаДанных = "ЭлементыБезКлючейПоЗначениямПолей" Тогда
		Если Элемент.ОбработатьУстаревшиеЭлементы Тогда
			Элемент = НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементы");
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если Элемент.ВидКлючаДанных = "ЭлементыБезКлючейПоПериоду" Тогда
		УстановитьЭлементСледующегоПериода(ПараметрыОбновления, Элемент, ДатаЭлемента);
		Если Элемент.КлючДанных = Null И Элемент.ОбработатьУстаревшиеЭлементы Тогда
			Элемент = НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементы");
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если Не ПараметрыОбновления.СписокСПериодом Тогда
		Элемент = НачальныйЭлемент(ПараметрыОбновления, "ЭлементыБезКлючейПоЗначениямПолей");
		Возврат;
	КонецЕсли;
	
	Элемент = НачальныйЭлемент(ПараметрыОбновления, "ЭлементыБезКлючейПоПериоду");
	ПоследнийОбновленныйЭлемент = ПараметрыОбновления.ПоследнийОбновленныйЭлемент;
	ПараметрыОбновления.ПоследнийОбновленныйЭлемент = Элемент;
	УстановитьЭлементСледующегоПериода(ПараметрыОбновления, Элемент, ДатаЭлемента, МаксимальнаяДата());
	ПараметрыОбновления.ПоследнийОбновленныйЭлемент = ПоследнийОбновленныйЭлемент;
	
	Если Элемент.КлючДанных = Null И Элемент.ОбработатьУстаревшиеЭлементы Тогда
		Элемент = НачальныйЭлемент(ПараметрыОбновления, "УстаревшиеЭлементы");
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры УточнитьПустойПоследнийЭлемент.
Процедура УстановитьЭлементСледующегоПериода(ПараметрыОбновления, Элемент, ДатаЭлемента,
			ДатаНачалаТекущегоПериода = Неопределено)
	
	Если ДатаНачалаТекущегоПериода = Неопределено Тогда
		ДатаНачалаТекущегоПериода = ПараметрыОбновления.ДатаНачала;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ДатаНачала", ДатаНачалаТекущегоПериода);
	
	Запрос.Текст = ПараметрыОбновления.ТекстЗапросаДатыСледующегоЭлементаДанных;
	
	РезультатЗапроса = Запрос.Выполнить();
	
	Если РезультатЗапроса.Пустой() Тогда
		Элемент.КлючДанных = Null;
		Возврат;
	КонецЕсли;
	
	Элемент = ПоследнийЭлемент(РезультатЗапроса.Выгрузить(), ПараметрыОбновления);
	ДатаЭлемента = ?(Элемент.Свойство("Дата"), Элемент.Дата, '00010101');
	
КонецПроцедуры

// Для функции ЭлементыДляОбновления.
Процедура УстановитьТекстЗапросаИПараметрыПоследнегоОбновленногоЭлементаДанных(Запрос, ПараметрыОбновления)
	
	Если ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
		Если ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "УстаревшиеЭлементы" Тогда
			Запрос.Текст = ПараметрыОбновления.ТекстЗапросаУстаревшихЭлементовДанных;
		Иначе
			Запрос.Текст = ПараметрыОбновления.ТекстЗапросаУстаревшихЭлементовДанныхИзОбщегоРегистра;
		КонецЕсли;
	Иначе
		Запрос.Текст = ПараметрыОбновления.ТекстЗапросаЭлементовДанныхСУстаревшимиКлючами;
		Если Не ЗначениеЗаполнено(ПараметрыОбновления.СоставПолей) Тогда
			Запрос.УстановитьПараметр("Список", ПараметрыОбновления.ИдентификаторСписка);
		КонецЕсли;
		Если ПараметрыОбновления.СписокСДатой Тогда
			Запрос.УстановитьПараметр("ДатаНачала",    ПараметрыОбновления.ДатаНачала);
			Запрос.УстановитьПараметр("ДатаОкончания", ПараметрыОбновления.ПоследнийОбновленныйЭлемент.Дата);
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	КлючДанных = ПараметрыОбновления.ПоследнийОбновленныйЭлемент.КлючДанных;
	
	Если ПараметрыОбновления.ЭтоСсылочныйТип Тогда
		УстановленныйКлючДанных = ?(ОбщегоНазначения.ЭтоСсылка(ТипЗнч(КлючДанных)), КлючДанных, Неопределено);
		Запрос.УстановитьПараметр("ПоследняяОбработаннаяСсылка", УстановленныйКлючДанных);
		Возврат;
	КонецЕсли;
	
	Запрос.УстановитьПараметр("ИдентификаторРегистра", ПараметрыОбновления.ИдентификаторСписка);
	УстановленныйКлючДанных = КлючДанных(ПараметрыОбновления, КлючДанных);
	ВидКлючаДанных = ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных;
	
	Если ПараметрыОбновления.СписокСПериодом
	   И СтрНачинаетсяС(ВидКлючаДанных, "ЭлементыБезКлючей") Тогда
		
		Если ВидКлючаДанных <> "ЭлементыБезКлючейПоПериоду" Тогда
			УстановитьВидКлючаДанных(ПараметрыОбновления.ПоследнийОбновленныйЭлемент,
				"ЭлементыБезКлючейПоПериоду");
		КонецЕсли;
		Запрос.Текст = ПараметрыОбновления.ТекстЗапросаЭлементовДанныхБезКлючейДоступа;
		Запрос.УстановитьПараметр("ДатаНачала",    ПараметрыОбновления.ДатаНачала);
		Запрос.УстановитьПараметр("ДатаОкончания", ПараметрыОбновления.ПоследнийОбновленныйЭлемент.Дата);
		
	ИначеЕсли СтрНачинаетсяС(ВидКлючаДанных, "ЭлементыБезКлючей") Тогда
		Если ВидКлючаДанных <> "ЭлементыБезКлючейПоЗначениямПолей" Тогда
			УстановитьВидКлючаДанных(ПараметрыОбновления.ПоследнийОбновленныйЭлемент,
				"ЭлементыБезКлючейПоЗначениямПолей");
		КонецЕсли;
		Запрос.Текст = ПараметрыОбновления.ТекстЗапросаЭлементовДанныхБезКлючейДоступа;
	КонецЕсли;
	
	Для Каждого КлючИЗначение Из УстановленныйКлючДанных Цикл
		Если КлючИЗначение.Значение = Неопределено Тогда
			ИмяПоля = КлючИЗначение.Ключ;
			Запрос.Текст = СтрЗаменить(Запрос.Текст, " > &" + ИмяПоля, " >= &" + ИмяПоля);
		КонецЕсли;
		Запрос.УстановитьПараметр(КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ПоследнийЭлемент, УстановитьПараметрыПоследнегоОбновленногоЭлемента и
// функции ТекстЗапросаКоличестваОставшихсяЭлементовРегистра.
//
// Возвращаемое значение:
//   Структура:
//     * Поле1 - ЛюбаяСсылка
//     * Поле2 - ЛюбаяСсылка
//     * Поле3 - ЛюбаяСсылка
//     * Поле4 - ЛюбаяСсылка
//     * Поле5 - ЛюбаяСсылка
//
Функция КлючДанных(ПараметрыОбновления, ИсходныйКлючДанных = Неопределено)
	
	КоличествоПолей = ПараметрыОбновления.ОпорныеПоля.Используемые.Количество();
	
	Если КоличествоПолей = 0
	 Или ПараметрыОбновления.ИспользуетсяОграничениеПоВладельцу
	 Или ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
		
		КоличествоПолей = ПараметрыОбновления.ОпорныеПоля.МаксимальноеКоличество;
	КонецЕсли;
	
	Если ТипЗнч(ИсходныйКлючДанных) = Тип("Структура")
	   И ИсходныйКлючДанных.Количество() = КоличествоПолей Тогда
		
		ЗначенияПолей = ИсходныйКлючДанных;
	Иначе
		ЗначенияПолей = Новый Структура;
	КонецЕсли;
	
	НовыеЗначенияПолей = Новый Структура;
	
	Для Номер = 1 По КоличествоПолей Цикл
		ИмяПоля = "Поле" + Номер;
		Если ЗначенияПолей.Свойство(ИмяПоля) Тогда
			НовыеЗначенияПолей.Вставить(ИмяПоля, ЗначенияПолей[ИмяПоля]);
		Иначе
			НовыеЗначенияПолей.Вставить(ИмяПоля, Неопределено);
		КонецЕсли;
	КонецЦикла;
	
	Возврат НовыеЗначенияПолей;
	
КонецФункции

// Для процедуры ЭлементыДляОбновления.
Функция НаборыГруппДоступаДляОбновления(ПараметрыОбновления, КоличествоВЗапросе)
	
	ПоследнийОбновленныйЭлемент = ПараметрыОбновления.ПоследнийОбновленныйЭлемент;
	ВидКлючаДанных = ПоследнийОбновленныйЭлемент.ВидКлючаДанных;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ПараметрыОбновления.ДляВнешнихПользователей);
	Запрос.УстановитьПараметр("ПоследняяОбработаннаяСсылка", ПоследнийОбновленныйЭлемент.КлючДанных);
	
	Если ВидКлючаДанных = "НаборыГруппСУстаревшимиПравами" Тогда
		Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка,
		|	ТекущийСписок.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка) КАК ЭтоНаборГруппДоступа
		|ИЗ
		|	Справочник.НаборыГруппДоступа КАК ТекущийСписок
		|ГДЕ
		|	ТекущийСписок.ДляВнешнихПользователей = &ДляВнешнихПользователей
		|	И ТекущийСписок.ТипЭлементовНабора В (ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка), ЗНАЧЕНИЕ(Справочник.ГруппыПользователей.ПустаяСсылка))
		|	И ТекущийСписок.Ссылка > &ПоследняяОбработаннаяСсылка
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Ссылка";
		
		Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст,
				"Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
		КонецЕсли;
		
	ИначеЕсли ВидКлючаДанных = "НовыеНаборыИзОдногоПользователя" Тогда
		Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
			ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
		Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	СоставыГруппПользователей.Пользователь КАК ТекущаяСсылка,
		|	СоставыГруппПользователей.Пользователь.Наименование КАК Наименование,
		|	СоставыГруппПользователей.Используется
		|		И ЕСТЬNULL(ВЫРАЗИТЬ(СоставыГруппПользователей.Пользователь КАК Справочник.Пользователи).ИдентификаторПользователяИБ, &ПустойУникальныйИдентификатор) <> &ПустойУникальныйИдентификатор КАК Используется,
		|	НаборыГруппДоступа.НеИспользуетсяС КАК НеИспользуетсяС
		|ИЗ
		|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
		|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
		|		ПО (НаборыГруппДоступа.Пользователь = СоставыГруппПользователей.Пользователь)
		|			И (НаборыГруппДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей)
		|			И (НаборыГруппДоступа.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка))
		|			И (НЕ ЛОЖЬ В
		|					(ВЫБРАТЬ ПЕРВЫЕ 1
		|						ЛОЖЬ
		|					ИЗ
		|						Справочник.НаборыГруппДоступа.Группы КАК ГруппыНаборов
		|					ГДЕ
		|						ГруппыНаборов.Ссылка = НаборыГруппДоступа.Ссылка))
		|ГДЕ
		|	ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.ГруппаПользователей) = ТИП(Справочник.Пользователи)
		|	И ВЫБОР
		|			КОГДА СоставыГруппПользователей.Используется
		|					И ВЫРАЗИТЬ(СоставыГруппПользователей.Пользователь КАК Справочник.Пользователи).ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор
		|				ТОГДА НаборыГруппДоступа.Пользователь ЕСТЬ NULL
		|						ИЛИ НаборыГруппДоступа.НеИспользуетсяС <> ДАТАВРЕМЯ(1, 1, 1)
		|			ИНАЧЕ НаборыГруппДоступа.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
		|		КОНЕЦ
		|	И СоставыГруппПользователей.Пользователь > &ПоследняяОбработаннаяСсылка
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	СоставыГруппПользователей.Пользователь";
		
		Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст,
				"Справочник.Пользователи", "Справочник.ВнешниеПользователи");
		КонецЕсли;
		
	ИначеЕсли ВидКлючаДанных = "НаборыГруппДоступаНазначенныеПользователям" Тогда
		Возврат НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГруппДоступа(
			ПараметрыОбновления, КоличествоВЗапросе);
		
	ИначеЕсли ВидКлючаДанных = "НаборыГруппПользователейНазначенныеПользователям" Тогда
		Возврат НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГруппПользователей(
			ПараметрыОбновления, КоличествоВЗапросе);
		
	ИначеЕсли ВидКлючаДанных = "НовыеНаборыГруппСУстаревшимиПравами" Тогда
		Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка,
		|	ТекущийСписок.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка) КАК ЭтоНаборГруппДоступа
		|ИЗ
		|	Справочник.НаборыГруппДоступа КАК ТекущийСписок
		|ГДЕ
		|	ТекущийСписок.ДляВнешнихПользователей = &ДляВнешнихПользователей
		|	И ТекущийСписок.ТипЭлементовНабора В (ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка), ЗНАЧЕНИЕ(Справочник.ГруппыПользователей.ПустаяСсылка))
		|	И (ИСТИНА В
		|				(ВЫБРАТЬ ПЕРВЫЕ 1
		|					ИСТИНА
		|				ИЗ
		|					Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
		|				ГДЕ
		|					НаборыГруппДоступа.НовыйНаборГруппДоступа = ТекущийСписок.Ссылка)
		|			ИЛИ ИСТИНА В
		|				(ВЫБРАТЬ ПЕРВЫЕ 1
		|					ИСТИНА
		|				ИЗ
		|					Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
		|				ГДЕ
		|					НаборыГруппДоступа.НовыйНаборГруппПользователей = ТекущийСписок.Ссылка))
		|	И ТекущийСписок.Ссылка > &ПоследняяОбработаннаяСсылка
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Ссылка";
		Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст,
				"Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
		КонецЕсли;
		
	ИначеЕсли ВидКлючаДанных = "НаборыГруппРазрешенныеПользователям" Тогда
		Запрос.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка
		|ИЗ
		|	Справочник.НаборыГруппДоступа КАК ТекущийСписок
		|ГДЕ
		|	ТекущийСписок.ДляВнешнихПользователей = &ДляВнешнихПользователей
		|	И ТекущийСписок.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
		|	И (ТекущийСписок.НовыйНаборГруппДоступа <> ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
		|				И НЕ ИСТИНА В
		|						(ВЫБРАТЬ ПЕРВЫЕ 1
		|							ИСТИНА
		|						ИЗ
		|							Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
		|						ГДЕ
		|							НаборыГруппДоступа.Ссылка = ТекущийСписок.НовыйНаборГруппДоступа
		|							И НаборыГруппДоступа.НовыйНаборГруппДоступа = ТекущийСписок.НовыйНаборГруппДоступа)
		|			ИЛИ ТекущийСписок.НовыйНаборГруппПользователей <> ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
		|				И НЕ ИСТИНА В
		|						(ВЫБРАТЬ ПЕРВЫЕ 1
		|							ИСТИНА
		|						ИЗ
		|							Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
		|						ГДЕ
		|							НаборыГруппДоступа.Ссылка = ТекущийСписок.НовыйНаборГруппПользователей
		|							И НаборыГруппДоступа.НовыйНаборГруппПользователей = ТекущийСписок.НовыйНаборГруппПользователей))
		|	И ТекущийСписок.Ссылка > &ПоследняяОбработаннаяСсылка
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Ссылка";
		Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст,
				"Справочник.Пользователи", "Справочник.ВнешниеПользователи");
		КонецЕсли;
		
	ИначеЕсли ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
		Возврат УстаревшиеНаборыГруппДоступаВСправочнике(ПараметрыОбновления, КоличествоВЗапросе);
	КонецЕсли;
	
	КоличествоВЗапросе = ?(КоличествоВЗапросе < 25, 25, ?(КоличествоВЗапросе > 10000, 10000, КоличествоВЗапросе));
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "995", Формат(КоличествоВЗапросе, "ЧГ="));
	УстановитьУточнениеПланаЗапроса(Запрос.Текст);
	
	ЭлементыДанных = Запрос.Выполнить().Выгрузить();
	
	Возврат ЭлементыДанных;
	
КонецФункции

// Для процедуры ОбновитьПорциюЭлементов.
Процедура ОбновитьНаборыГруппДоступа(ЭлементыДанных, ПараметрыОбновления)
	
	ВидКлючаДанных = ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных;
	
	Если ВидКлючаДанных = "НаборыГруппСУстаревшимиПравами" Тогда
		ОбновитьНаборыГруппСУстаревшимиПравами(ЭлементыДанных, ПараметрыОбновления);
		
	ИначеЕсли ВидКлючаДанных = "НовыеНаборыИзОдногоПользователя" Тогда
		ОбновитьНаборыИзОдногоПользователяВСправочнике(ЭлементыДанных, ПараметрыОбновления);
		
	ИначеЕсли ВидКлючаДанных = "НаборыГруппДоступаНазначенныеПользователям" Тогда
		ОбновитьНаборыГруппНазначенныеПользователямВСправочнике(ЭлементыДанных, ПараметрыОбновления, Истина);
		
	ИначеЕсли ВидКлючаДанных = "НаборыГруппПользователейНазначенныеПользователям" Тогда
		ОбновитьНаборыГруппНазначенныеПользователямВСправочнике(ЭлементыДанных, ПараметрыОбновления, Ложь);
		
	ИначеЕсли ВидКлючаДанных = "НовыеНаборыГруппСУстаревшимиПравами" Тогда
		ОбновитьНаборыГруппСУстаревшимиПравами(ЭлементыДанных, ПараметрыОбновления, Истина);
		
	ИначеЕсли ВидКлючаДанных = "НаборыГруппРазрешенныеПользователям" Тогда
		ОбновитьНаборыГруппРазрешенныеПользователямВСправочнике(ЭлементыДанных, ПараметрыОбновления);
		
	ИначеЕсли ЭтоОбработкаУстаревшихЭлементов(ПараметрыОбновления) Тогда
		ОбработатьУстаревшиеНаборыВСправочнике(ЭлементыДанных, ПараметрыОбновления);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура ОчиститьПраваПустогоНабораГруппДоступа(ПараметрыОбновления)
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК КлючиДоступаНаборовГруппДоступа
	|ГДЕ
	|	КлючиДоступаНаборовГруппДоступа.НаборГруппДоступа = ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.КлючиДоступаПользователей КАК КлючиДоступаПользователей
	|ГДЕ
	|	КлючиДоступаПользователей.Пользователь = ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.КлючиДоступаВнешнихПользователей КАК КлючиДоступаВнешнихПользователей
	|ГДЕ
	|	КлючиДоступаВнешнихПользователей.ВнешнийПользователь = ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)";
	
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	
	Если Не РезультатыЗапроса[0].Пустой() Тогда
		НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаНаборовГруппДоступа);
		НаборЗаписей.Отбор.НаборГруппДоступа.Установить(Справочники.НаборыГруппДоступа.ПустаяСсылка());
		НаборЗаписей.Записать();
	КонецЕсли;
	
	Если Не РезультатыЗапроса[1].Пустой() Тогда
		НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаПользователей);
		НаборЗаписей.Отбор.Пользователь.Установить(Справочники.НаборыГруппДоступа.ПустаяСсылка());
		НаборЗаписей.Записать();
	КонецЕсли;
	
	Если Не РезультатыЗапроса[2].Пустой() Тогда
		НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаВнешнихПользователей);
		НаборЗаписей.Отбор.ВнешнийПользователь.Установить(Справочники.НаборыГруппДоступа.ПустаяСсылка());
		НаборЗаписей.Записать();
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбновитьНаборыГруппДоступа.
Процедура ОбновитьНаборыГруппСУстаревшимиПравами(ЭлементыДанных, ПараметрыОбновления, ЭтоНовыеНаборы = Ложь)
	
	Если ЭтоНовыеНаборы Тогда
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
	КонецЕсли;
	
	Для Каждого ЭлементДанных Из ЭлементыДанных Цикл
		Если ЭлементДанных.ЭтоНаборГруппДоступа Тогда
			// @skip-check query-in-loop - Порционная обработка данных
			ОбновитьКлючиДоступаНаборовГрупп(ПараметрыОбновления, ЭлементДанных.ТекущаяСсылка,
				"КлючиДоступаНаборовГруппДоступа", "ГруппыДоступа", "НаборГруппДоступа");
			
		ИначеЕсли Не ПараметрыОбновления.ДляВнешнихПользователей Тогда
			// @skip-check query-in-loop - Порционная обработка данных
			ОбновитьКлючиДоступаНаборовГрупп(ПараметрыОбновления, ЭлементДанных.ТекущаяСсылка,
				"КлючиДоступаПользователей", "ГруппыПользователей", "Пользователь");
		Иначе
			// @skip-check query-in-loop - Порционная обработка данных
			ОбновитьКлючиДоступаНаборовГрупп(ПараметрыОбновления, ЭлементДанных.ТекущаяСсылка,
				"КлючиДоступаВнешнихПользователей", "ГруппыВнешнихПользователей", "ВнешнийПользователь");
		КонецЕсли;
		
		Если ЭтоНовыеНаборы Тогда
			ЭлементБлокировки.УстановитьЗначение("Ссылка", ЭлементДанных.ТекущаяСсылка);
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				Объект = СлужебныйЭлемент(Неопределено, ЭлементДанных.ТекущаяСсылка);
				Если Объект <> Неопределено Тогда
					ИмяРеквизита = ?(ЭлементДанных.ЭтоНаборГруппДоступа,
						"НовыйНаборГруппДоступа", "НовыйНаборГруппПользователей");
					Если ЗначениеЗаполнено(Объект[ИмяРеквизита]) Тогда
						Объект[ИмяРеквизита] = ПараметрыОбновления.ПустойНаборГруппДоступа;
						Объект.Записать();
					КонецЕсли;
				КонецЕсли;
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЕсли;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 1) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьНаборыГруппСУстаревшимиПравами.
Процедура ОбновитьКлючиДоступаНаборовГрупп(ПараметрыОбновления, НаборГруппДоступа, ИмяРегистраПрав,
				ИмяСправочникаГрупп, ИмяПоляНабораГрупп)
	
	Если НаборГруппДоступа = УправлениеДоступомСлужебныйПовтИсп.РазрешенныйПустойНаборГруппДоступа() Тогда
		ТекстЗапросаВыбораПорцииДляБлокировки =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки,
		|	ВсеСтроки.КлючДоступа КАК КлючДоступа
		|ИЗ
		|	(ВЫБРАТЬ
		|		КлючиДоступаГруппДоступа.КлючДоступа КАК КлючДоступа,
		|		КлючиДоступаГруппДоступа.ПравоИзменение КАК ПравоИзменение,
		|		КлючиДоступаГруппДоступа.ПравоДобавление КАК ПравоДобавление,
		|		1 КАК ВидИзмененияСтроки
		|	ИЗ
		|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
		|	ГДЕ
		|		КлючиДоступаГруппДоступа.ГруппаДоступа = ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка)
		|		И &УточнениеПланаЗапроса
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		СтарыеДанные.КлючДоступа,
		|		СтарыеДанные.ПравоИзменение,
		|		СтарыеДанные.ПравоДобавление,
		|		-1
		|	ИЗ
		|		РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК СтарыеДанные
		|	ГДЕ
		|		&УсловиеОтбораПравГрупп
		|		И СтарыеДанные.НаборГруппДоступа = &НаборГруппДоступа) КАК ВсеСтроки
		|
		|СГРУППИРОВАТЬ ПО
		|	ВсеСтроки.КлючДоступа,
		|	ВсеСтроки.ПравоИзменение,
		|	ВсеСтроки.ПравоДобавление
		|
		|ИМЕЮЩИЕ
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
		|
		|УПОРЯДОЧИТЬ ПО
		|	ВидИзмененияСтроки,
		|	КлючДоступа";
		
		ТекстЗапросаВыбораПорцииДляОбновления =
		"ВЫБРАТЬ ПЕРВЫЕ 100
		|	ВсеСтроки.КлючДоступа КАК КлючДоступа,
		|	ВсеСтроки.ПравоИзменение КАК ПравоИзменение,
		|	ВсеСтроки.ПравоДобавление КАК ПравоДобавление,
		|	ИСТИНА КАК ЭтоПраваНабораГрупп,
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки
		|ИЗ
		|	(ВЫБРАТЬ
		|		КлючиДоступаГруппДоступа.КлючДоступа КАК КлючДоступа,
		|		КлючиДоступаГруппДоступа.ПравоИзменение КАК ПравоИзменение,
		|		КлючиДоступаГруппДоступа.ПравоДобавление КАК ПравоДобавление,
		|		1 КАК ВидИзмененияСтроки
		|	ИЗ
		|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
		|	ГДЕ
		|		КлючиДоступаГруппДоступа.ГруппаДоступа = ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка)
		|		И КлючиДоступаГруппДоступа.КлючДоступа В(&КлючиДоступа)
		|		И &УточнениеПланаЗапроса
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		СтарыеДанные.КлючДоступа,
		|		СтарыеДанные.ПравоИзменение,
		|		СтарыеДанные.ПравоДобавление,
		|		-1
		|	ИЗ
		|		РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК СтарыеДанные
		|	ГДЕ
		|		&УсловиеОтбораПравГрупп
		|		И СтарыеДанные.НаборГруппДоступа = &НаборГруппДоступа
		|		И СтарыеДанные.КлючДоступа В(&КлючиДоступа)) КАК ВсеСтроки
		|
		|СГРУППИРОВАТЬ ПО
		|	ВсеСтроки.КлючДоступа,
		|	ВсеСтроки.ПравоИзменение,
		|	ВсеСтроки.ПравоДобавление
		|
		|ИМЕЮЩИЕ
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
		|
		|УПОРЯДОЧИТЬ ПО
		|	ВидИзмененияСтроки";
	Иначе
		ТекстЗапросаВыбораПорцииДляБлокировки =
		"ВЫБРАТЬ ПЕРВЫЕ 1000
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки,
		|	ВсеСтроки.КлючДоступа КАК КлючДоступа
		|ИЗ
		|	(ВЫБРАТЬ
		|		КлючиДоступаГруппДоступа.КлючДоступа КАК КлючДоступа,
		|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоИзменение) КАК ПравоИзменение,
		|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоДобавление) КАК ПравоДобавление,
		|		1 КАК ВидИзмененияСтроки
		|	ИЗ
		|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
		|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыВходящиеВНаборы
		|			ПО (ГруппыВходящиеВНаборы.Группа = КлючиДоступаГруппДоступа.ГруппаДоступа)
		|				И (ТИПЗНАЧЕНИЯ(ГруппыВходящиеВНаборы.Группа) = ТИП(Справочник.ГруппыДоступа))
		|				И (ТИПЗНАЧЕНИЯ(КлючиДоступаГруппДоступа.ГруппаДоступа) = ТИП(Справочник.ГруппыДоступа))
		|				И (ГруппыВходящиеВНаборы.Ссылка = &НаборГруппДоступа)
		|				И (&УточнениеПланаЗапроса)
		|	
		|	СГРУППИРОВАТЬ ПО
		|		КлючиДоступаГруппДоступа.КлючДоступа
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		СтарыеДанные.КлючДоступа,
		|		СтарыеДанные.ПравоИзменение,
		|		СтарыеДанные.ПравоДобавление,
		|		-1
		|	ИЗ
		|		РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК СтарыеДанные
		|	ГДЕ
		|		&УсловиеОтбораПравГрупп
		|		И СтарыеДанные.НаборГруппДоступа = &НаборГруппДоступа) КАК ВсеСтроки
		|
		|СГРУППИРОВАТЬ ПО
		|	ВсеСтроки.КлючДоступа,
		|	ВсеСтроки.ПравоИзменение,
		|	ВсеСтроки.ПравоДобавление
		|
		|ИМЕЮЩИЕ
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
		|
		|УПОРЯДОЧИТЬ ПО
		|	ВидИзмененияСтроки,
		|	КлючДоступа";
		
		ТекстЗапросаВыбораПорцииДляОбновления =
		"ВЫБРАТЬ ПЕРВЫЕ 100
		|	ВсеСтроки.КлючДоступа КАК КлючДоступа,
		|	ВсеСтроки.ПравоИзменение КАК ПравоИзменение,
		|	ВсеСтроки.ПравоДобавление КАК ПравоДобавление,
		|	ИСТИНА КАК ЭтоПраваНабораГрупп,
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки
		|ИЗ
		|	(ВЫБРАТЬ
		|		КлючиДоступаГруппДоступа.КлючДоступа КАК КлючДоступа,
		|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоИзменение) КАК ПравоИзменение,
		|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоДобавление) КАК ПравоДобавление,
		|		1 КАК ВидИзмененияСтроки
		|	ИЗ
		|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
		|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыВходящиеВНаборы
		|			ПО (ГруппыВходящиеВНаборы.Группа = КлючиДоступаГруппДоступа.ГруппаДоступа)
		|				И (ТИПЗНАЧЕНИЯ(ГруппыВходящиеВНаборы.Группа) = ТИП(Справочник.ГруппыДоступа))
		|				И (ТИПЗНАЧЕНИЯ(КлючиДоступаГруппДоступа.ГруппаДоступа) = ТИП(Справочник.ГруппыДоступа))
		|				И (ГруппыВходящиеВНаборы.Ссылка = &НаборГруппДоступа)
		|				И (КлючиДоступаГруппДоступа.КлючДоступа В (&КлючиДоступа))
		|				И (&УточнениеПланаЗапроса)
		|	
		|	СГРУППИРОВАТЬ ПО
		|		КлючиДоступаГруппДоступа.КлючДоступа
		|	
		|	ОБЪЕДИНИТЬ ВСЕ
		|	
		|	ВЫБРАТЬ
		|		СтарыеДанные.КлючДоступа,
		|		СтарыеДанные.ПравоИзменение,
		|		СтарыеДанные.ПравоДобавление,
		|		-1
		|	ИЗ
		|		РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК СтарыеДанные
		|	ГДЕ
		|		&УсловиеОтбораПравГрупп
		|		И СтарыеДанные.НаборГруппДоступа = &НаборГруппДоступа
		|		И СтарыеДанные.КлючДоступа В(&КлючиДоступа)) КАК ВсеСтроки
		|
		|СГРУППИРОВАТЬ ПО
		|	ВсеСтроки.КлючДоступа,
		|	ВсеСтроки.ПравоИзменение,
		|	ВсеСтроки.ПравоДобавление
		|
		|ИМЕЮЩИЕ
		|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
		|
		|УПОРЯДОЧИТЬ ПО
		|	ВидИзмененияСтроки";
	КонецЕсли;
	
	ТекстЗапросаВыбораПорцииДляБлокировки = СтрЗаменить(ТекстЗапросаВыбораПорцииДляБлокировки,
		"ТИП(Справочник.ГруппыДоступа)", "ТИП(Справочник." + ИмяСправочникаГрупп + ")"); // @query-part-1 @query-part-2
	
	ТекстЗапросаВыбораПорцииДляОбновления = СтрЗаменить(ТекстЗапросаВыбораПорцииДляОбновления,
		"ТИП(Справочник.ГруппыДоступа)", "ТИП(Справочник." + ИмяСправочникаГрупп + ")"); // @query-part-1 @query-part-2
	
	ТекстЗапросаВыбораПорцииДляОбновления = СтрЗаменить(ТекстЗапросаВыбораПорцииДляОбновления,
		"СтарыеДанные.НаборГруппДоступа", "СтарыеДанные." + ИмяПоляНабораГрупп);
	
	ТекстЗапросаВыбораПорцииДляБлокировки = СтрЗаменить(ТекстЗапросаВыбораПорцииДляБлокировки,
		"СтарыеДанные.НаборГруппДоступа", "СтарыеДанные." + ИмяПоляНабораГрупп);
	
	ТекстЗапросаВыбораПорцииДляБлокировки = СтрЗаменить(ТекстЗапросаВыбораПорцииДляБлокировки,
		"РегистрСведений.КлючиДоступаНаборовГруппДоступа", "РегистрСведений." + ИмяРегистраПрав);
	
	ТекстЗапросаВыбораПорцииДляОбновления = СтрЗаменить(ТекстЗапросаВыбораПорцииДляОбновления,
		"РегистрСведений.КлючиДоступаНаборовГруппДоступа", "РегистрСведений." + ИмяРегистраПрав);
	
	ТекстЗапросаВыбораПорцииДляБлокировки = СтрЗаменить(ТекстЗапросаВыбораПорцииДляБлокировки,
		"&УсловиеОтбораПравГрупп", ?(ИмяСправочникаГрупп = "ГруппыДоступа", "ИСТИНА", "СтарыеДанные.ЭтоПраваНабораГрупп"));
	
	ТекстЗапросаВыбораПорцииДляОбновления = СтрЗаменить(ТекстЗапросаВыбораПорцииДляОбновления,
		"&УсловиеОтбораПравГрупп", ?(ИмяСправочникаГрупп = "ГруппыДоступа", "ИСТИНА", "СтарыеДанные.ЭтоПраваНабораГрупп"));
	
	Пока Истина Цикл
		Запрос = Новый Запрос;
		Запрос.Текст = ТекстЗапросаВыбораПорцииДляБлокировки;
		УстановитьУточнениеПланаЗапроса(Запрос.Текст, Истина);
		Запрос.УстановитьПараметр("НаборГруппДоступа", НаборГруппДоступа);
		
		Выгрузка = Запрос.Выполнить().Выгрузить();
		Если Выгрузка.Количество() = 0 Тогда
			Прервать;
		КонецЕсли;
		
		КлючиДоступа = Новый Массив;
		ПорцииКлючейДоступа = Новый Массив;
		Для Каждого Строка Из Выгрузка Цикл
			Если КлючиДоступа.Количество() > 100 Тогда
				ПорцииКлючейДоступа.Добавить(КлючиДоступа);
				КлючиДоступа = Новый Массив;
			КонецЕсли;
			КлючиДоступа.Добавить(Строка.КлючДоступа);
		КонецЦикла;
		ПорцииКлючейДоступа.Добавить(КлючиДоступа);
		
		Для Каждого КлючиДоступа Из ПорцииКлючейДоступа Цикл
			Блокировка = Новый БлокировкаДанных;
			Для Каждого КлючДоступа Из КлючиДоступа Цикл
				ЭлементБлокировки = Блокировка.Добавить("РегистрСведений." + ИмяРегистраПрав);
				ЭлементБлокировки.УстановитьЗначение(ИмяПоляНабораГрупп, НаборГруппДоступа);
				ЭлементБлокировки.УстановитьЗначение("КлючДоступа",      КлючДоступа);
			КонецЦикла;
			
			Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
				ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
				ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
				ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаГруппДоступа");
				ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
			КонецЕсли;
			
			Запрос = Новый Запрос;
			Запрос.Текст = ТекстЗапросаВыбораПорцииДляОбновления;
			УстановитьУточнениеПланаЗапроса(Запрос.Текст, Истина);
			Запрос.УстановитьПараметр("НаборГруппДоступа", НаборГруппДоступа);
			Запрос.УстановитьПараметр("КлючиДоступа", КлючиДоступа);
			
			НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений[ИмяРегистраПрав]);
			
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				// @skip-check query-in-loop - Порционная обработка данных
				Выборка = Запрос.Выполнить().Выбрать();
				
				УдалениеЗавершено = Ложь;
				Пока Выборка.Следующий() Цикл
					ЭлементОтбора = НаборЗаписей.Отбор[ИмяПоляНабораГрупп]; // ЭлементОтбора
					ЭлементОтбора.Установить(НаборГруппДоступа);
					НаборЗаписей.Отбор.КлючДоступа.Установить(Выборка.КлючДоступа);
					Если Не УдалениеЗавершено И Выборка.ВидИзмененияСтроки = 1 Тогда
						УдалениеЗавершено = Истина;
						ОднаЗапись = НаборЗаписей.Добавить();
						ОднаЗапись[ИмяПоляНабораГрупп] = НаборГруппДоступа;
					КонецЕсли;
					Если УдалениеЗавершено Тогда
						ЗаполнитьЗначенияСвойств(ОднаЗапись, Выборка);
					КонецЕсли;
					НаборЗаписей.Записать();
				КонецЦикла;
				
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура УстранитьДублиНаборовИзОдногоПользователяВСправочнике(ПараметрыОбновления)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ПараметрыОбновления.ДляВнешнихПользователей);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НаборыГруппДоступа.Пользователь КАК Пользователь,
	|	НаборыГруппДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
	|ГДЕ
	|	НаборыГруппДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И НаборыГруппДоступа.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
	|	И НаборыГруппДоступа.Пользователь В
	|			(ВЫБРАТЬ
	|				ВЫРАЗИТЬ(НаборыГруппДоступа.Пользователь КАК Справочник.Пользователи) КАК Пользователь
	|			ИЗ
	|				Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
	|			ГДЕ
	|				НаборыГруппДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|				И НаборыГруппДоступа.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
	|			СГРУППИРОВАТЬ ПО
	|				НаборыГруппДоступа.Пользователь
	|			ИМЕЮЩИЕ
	|				КОЛИЧЕСТВО(НаборыГруппДоступа.Пользователь) > 1)";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "Справочник.Пользователи", "Справочник.ВнешниеПользователи");
	КонецЕсли;
	
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Если Выборка.Ссылка.УникальныйИдентификатор() = Выборка.Пользователь.УникальныйИдентификатор() Тогда
			Продолжить;
		КонецЕсли;
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			Объект = СлужебныйЭлемент(Неопределено, Выборка.Ссылка);
			Если Объект <> Неопределено Тогда
				Объект.ТипЭлементовНабора = Неопределено;
				Объект.Записать();
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьНаборыГруппДоступа.
Процедура ОбновитьНаборыИзОдногоПользователяВСправочнике(ЭлементыДанных, ПараметрыОбновления)
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		ТипЭлементовНабора = Справочники.ВнешниеПользователи.ПустаяСсылка();
		ПредставлениеЭлемента = НСтр("ru = 'Внешний пользователь'", ОбщегоНазначения.КодОсновногоЯзыка());
	Иначе
		ТипЭлементовНабора = Справочники.Пользователи.ПустаяСсылка();
		ПредставлениеЭлемента = НСтр("ru = 'Пользователь'", ОбщегоНазначения.КодОсновногоЯзыка());
	КонецЕсли;
	
	Для Каждого Строка Из ЭлементыДанных Цикл
		СсылкаНабора = Справочники.НаборыГруппДоступа.ПолучитьСсылку(Строка.ТекущаяСсылка.УникальныйИдентификатор());
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
		ЭлементБлокировки.УстановитьЗначение("Ссылка", СсылкаНабора);
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			
			Объект = СлужебныйЭлемент(Неопределено, СсылкаНабора);
			Если Объект = Неопределено Тогда
				Объект = СлужебныйЭлемент(Справочники.НаборыГруппДоступа);
				Объект.УстановитьСсылкуНового(СсылкаНабора);
			Иначе
				Объект.Группы.Очистить();
			КонецЕсли;
			Если Строка.Используется Тогда
				Объект.НеИспользуетсяС = '00010101';
			ИначеЕсли Не ЗначениеЗаполнено(Строка.НеИспользуетсяС) Тогда
				Объект.НеИспользуетсяС = ТекущаяДатаСеанса();
			КонецЕсли;
			Объект.ДляВнешнихПользователей = ПараметрыОбновления.ДляВнешнихПользователей;
			Объект.ТипЭлементовНабора = ТипЭлементовНабора;
			Объект.Пользователь = Строка.ТекущаяСсылка;
			ОписаниеОбъекта = Строка; // СправочникОбъект.Пользователи
			Объект.Наименование = Строка(ОписаниеОбъекта.Наименование) + " (" + ПредставлениеЭлемента + ")";
			Объект.Записать();
			
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 1) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура ЗаполнитьПустыеХешиНаборовГрупп(ПараметрыОбновления)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ПараметрыОбновления.ДляВнешнихПользователей);
	Запрос.УстановитьПараметр("РазрешенныйПустойНабор",
		УправлениеДоступомСлужебныйПовтИсп.РазрешенныйПустойНаборГруппДоступа());
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НаборыГруппДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
	|ГДЕ
	|	НаборыГруппДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И НаборыГруппДоступа.ТипЭлементовНабора В (ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка), ЗНАЧЕНИЕ(Справочник.ГруппыПользователей.ПустаяСсылка))
	|	И НаборыГруппДоступа.Хеш = 0
	|	И НаборыГруппДоступа.Ссылка <> &РазрешенныйПустойНабор";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
	КонецЕсли;
	
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			Объект = СлужебныйЭлемент(Неопределено, Выборка.Ссылка);
			Если Объект <> Неопределено Тогда
				ЗаполнитьХешНабораГрупп(Объект);
				Объект.Записать();
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

// Для функции НаборыГруппДоступаДляОбновления.
Функция НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГруппДоступа(ПараметрыОбновления,
			КоличествоВЗапросе)
	
	Если Не ПараметрыОбновления.ДляВнешнихПользователей Тогда
		ТекстЗапросаГрупп =
		"ВЫБРАТЬ
		|	ГруппыДоступа.Ссылка КАК СсылкаГруппы
		|ИЗ
		|	Справочник.ГруппыДоступа КАК ГруппыДоступа
		|ГДЕ
		|	ГруппыДоступа.Профиль <> &ПрофильАдминистратор
		|	И НЕ ГруппыДоступа.ПометкаУдаления
		|	И НЕ ГруппыДоступа.Профиль.ПометкаУдаления
		|	И ИСТИНА В
		|			(ВЫБРАТЬ ПЕРВЫЕ 1
		|				ИСТИНА
		|			ИЗ
		|				Справочник.ГруппыДоступа.Пользователи КАК ПользователиГруппДоступа
		|			ГДЕ
		|				ПользователиГруппДоступа.Ссылка = ГруппыДоступа.Ссылка)
		|	И ВЫБОР
		|			КОГДА ИСТИНА В
		|					(ВЫБРАТЬ ПЕРВЫЕ 1
		|						ИСТИНА
		|					ИЗ
		|						Справочник.ПрофилиГруппДоступа.Назначение КАК Назначение
		|					ГДЕ
		|						Назначение.Ссылка = ГруппыДоступа.Профиль
		|						И ТИПЗНАЧЕНИЯ(Назначение.ТипПользователей) = ТИП(Справочник.Пользователи))
		|				ТОГДА ИСТИНА
		|			КОГДА НЕ ЛОЖЬ В
		|						(ВЫБРАТЬ ПЕРВЫЕ 1
		|							ЛОЖЬ
		|						ИЗ
		|							Справочник.ПрофилиГруппДоступа.Назначение КАК Назначение
		|						ГДЕ
		|							Назначение.Ссылка = ГруппыДоступа.Профиль)
		|				ТОГДА ИСТИНА
		|			ИНАЧЕ ЛОЖЬ
		|		КОНЕЦ
		|
		|УПОРЯДОЧИТЬ ПО
		|	Ссылка";
	Иначе
		ТекстЗапросаГрупп =
		"ВЫБРАТЬ
		|	ГруппыДоступа.Ссылка КАК СсылкаГруппы
		|ИЗ
		|	Справочник.ГруппыДоступа КАК ГруппыДоступа
		|ГДЕ
		|	ГруппыДоступа.Профиль <> &ПрофильАдминистратор
		|	И НЕ ГруппыДоступа.ПометкаУдаления
		|	И НЕ ГруппыДоступа.Профиль.ПометкаУдаления
		|	И ИСТИНА В
		|			(ВЫБРАТЬ ПЕРВЫЕ 1
		|				ИСТИНА
		|			ИЗ
		|				Справочник.ГруппыДоступа.Пользователи КАК ПользователиГруппДоступа
		|			ГДЕ
		|				ПользователиГруппДоступа.Ссылка = ГруппыДоступа.Ссылка)
		|	И ИСТИНА В
		|			(ВЫБРАТЬ ПЕРВЫЕ 1
		|				ИСТИНА
		|			ИЗ
		|				Справочник.ПрофилиГруппДоступа.Назначение КАК Назначение
		|			ГДЕ
		|				Назначение.Ссылка = ГруппыДоступа.Профиль
		|				И Назначение.ТипПользователей <> НЕОПРЕДЕЛЕНО
		|				И ТИПЗНАЧЕНИЯ(Назначение.ТипПользователей) <> ТИП(Справочник.Пользователи))
		|
		|УПОРЯДОЧИТЬ ПО
		|	Ссылка";
	КонецЕсли;
	
	ТекстЗапросаНовыхНаборовГрупп =
	"ВЫБРАТЬ
	|	ГруппыДоступаПользователей.Пользователь КАК Пользователь,
	|	СУММА(НомераГрупп.ЧастьНомера1) КАК ЧастьНомера1
	|ПОМЕСТИТЬ НовыеНаборыГрупп
	|ИЗ
	|	(ВЫБРАТЬ РАЗЛИЧНЫЕ
	|		СоставыГруппПользователей.Пользователь КАК Пользователь,
	|		ПользователиГруппДоступа.Ссылка КАК ГруппаДоступа
	|	ИЗ
	|		Справочник.ГруппыДоступа.Пользователи КАК ПользователиГруппДоступа
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|			ПО (СоставыГруппПользователей.ГруппаПользователей = ПользователиГруппДоступа.Пользователь)
	|				И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.Пользователь) = ТИП(Справочник.Пользователи))
	|				И (ВЫРАЗИТЬ(СоставыГруппПользователей.Пользователь КАК Справочник.Пользователи).ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)
	|				И (СоставыГруппПользователей.Используется)) КАК ГруппыДоступаПользователей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ НомераГрупп КАК НомераГрупп
	|		ПО ГруппыДоступаПользователей.ГруппаДоступа = НомераГрупп.Группа
	|
	|СГРУППИРОВАТЬ ПО
	|	ГруппыДоступаПользователей.Пользователь
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	ГруппыДоступаПользователей.Пользователь";
	
	ЭлементыДанных = НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГрупп(ПараметрыОбновления,
		ТекстЗапросаГрупп, ТекстЗапросаНовыхНаборовГрупп, "НаборГруппДоступа", "ГруппыДоступа");
	
	КоличествоВЗапросе = ЭлементыДанных.Количество();
	Возврат ЭлементыДанных;
	
КонецФункции

// Для функции НаборыГруппДоступаДляОбновления.
Функция НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГруппПользователей(ПараметрыОбновления,
			КоличествоВЗапросе)
	
	ТекстЗапросаГрупп =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	СоставыГруппПользователей.ГруппаПользователей КАК СсылкаГруппы
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|ГДЕ
	|	СоставыГруппПользователей.Используется
	|	И ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.ГруппаПользователей) = ТИП(Справочник.ГруппыПользователей)
	|
	|УПОРЯДОЧИТЬ ПО
	|	СоставыГруппПользователей.ГруппаПользователей";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		ТекстЗапросаГрупп = СтрЗаменить(ТекстЗапросаГрупп,
			"Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
	КонецЕсли;
	
	ТекстЗапросаНовыхНаборовГрупп =
	"ВЫБРАТЬ
	|	СоставыГруппПользователей.Пользователь КАК Пользователь,
	|	СУММА(НомераГрупп.ЧастьНомера1) КАК ЧастьНомера1
	|ПОМЕСТИТЬ НовыеНаборыГрупп
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ НомераГрупп КАК НомераГрупп
	|		ПО СоставыГруппПользователей.ГруппаПользователей = НомераГрупп.Группа
	|			И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.Пользователь) = ТИП(Справочник.Пользователи))
	|			И (ВЫРАЗИТЬ(СоставыГруппПользователей.Пользователь КАК Справочник.Пользователи).ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)
	|			И (СоставыГруппПользователей.Используется)
	|
	|СГРУППИРОВАТЬ ПО
	|	СоставыГруппПользователей.Пользователь
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	СоставыГруппПользователей.Пользователь";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		ИмяСправочникаГрупп = "ГруппыВнешнихПользователей";
	Иначе
		ИмяСправочникаГрупп = "ГруппыПользователей";
	КонецЕсли;
	
	ЭлементыДанных = НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГрупп(ПараметрыОбновления,
		ТекстЗапросаГрупп, ТекстЗапросаНовыхНаборовГрупп, "НаборГруппПользователей", ИмяСправочникаГрупп);
	
	КоличествоВЗапросе = ЭлементыДанных.Количество();
	Возврат ЭлементыДанных;
	
КонецФункции

// Для функций НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГруппДоступа,
// НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГруппПользователей.
//
Функция НаборыИзОдногоПользователяДляОбновленияНазначенныхНаборовГрупп(ПараметрыОбновления,
			ТекстЗапросаГрупп, ТекстЗапросаНовыхНаборовГрупп, ИмяПоляНабора, ИмяСправочникаГрупп)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ПрофильАдминистратор", УправлениеДоступом.ПрофильАдминистратор());
	Запрос.Текст = ТекстЗапросаГрупп;
	НомераГрупп = Запрос.Выполнить().Выгрузить();
	
	ИменаЧастейНомера = Новый Массив;
	МаксимальныйНомерГруппыВЧастиНомера = 9223372036854775808; // 2^63.
	ЗаполнитьНомераГрупп(НомераГрупп, ИменаЧастейНомера, МаксимальныйНомерГруппыВЧастиНомера);
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("НомераГрупп", НомераГрупп);
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ПараметрыОбновления.ДляВнешнихПользователей);
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НомераГрупп.СсылкаГруппы КАК Группа,
	|	НомераГрупп.ЧастьНомера1 КАК ЧастьНомера1
	|ПОМЕСТИТЬ НомераГрупп
	|ИЗ
	|	&НомераГрупп КАК НомераГрупп
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НЕОПРЕДЕЛЕНО КАК Пользователь,
	|	СУММА(НомераГрупп.ЧастьНомера1) КАК ЧастьНомера1
	|ПОМЕСТИТЬ НовыеНаборыГрупп
	|ИЗ
	|	НомераГрупп КАК НомераГрупп
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НаборыГрупп.Ссылка КАК НаборИзОдногоПользователя,
	|	НаборыГрупп.Пользователь КАК Пользователь,
	|	НаборыГрупп.РазрешенныйНаборГруппДоступа КАК ТекущийНаборГрупп,
	|	СУММА(ЕСТЬNULL(НомераГрупп.ЧастьНомера1, -1234567)) КАК ЧастьНомера1
	|ПОМЕСТИТЬ СтарыеНаборыГрупп
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГрупп
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыНаборов
	|		ПО (ГруппыНаборов.Ссылка = НаборыГрупп.РазрешенныйНаборГруппДоступа)
	|		ЛЕВОЕ СОЕДИНЕНИЕ НомераГрупп КАК НомераГрупп
	|		ПО (ГруппыНаборов.Группа = НомераГрупп.Группа)
	|ГДЕ
	|	НаборыГрупп.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И НаборыГрупп.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
	|	И НаборыГрупп.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
	|	И НаборыГрупп.НовыйНаборГруппДоступа = ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
	|	И &УточнениеПланаЗапроса
	|
	|СГРУППИРОВАТЬ ПО
	|	НаборыГрупп.Ссылка,
	|	НаборыГрупп.Пользователь,
	|	НаборыГрупп.РазрешенныйНаборГруппДоступа
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	НаборыГрупп.Ссылка,
	|	НаборыГрупп.Пользователь,
	|	НаборыГрупп.НовыйНаборГруппДоступа,
	|	СУММА(ЕСТЬNULL(НомераГрупп.ЧастьНомера1, -1234567))
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГрупп
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыНаборов
	|		ПО (ГруппыНаборов.Ссылка = НаборыГрупп.НовыйНаборГруппДоступа)
	|		ЛЕВОЕ СОЕДИНЕНИЕ НомераГрупп КАК НомераГрупп
	|		ПО (ГруппыНаборов.Группа = НомераГрупп.Группа)
	|ГДЕ
	|	НаборыГрупп.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И НаборыГрупп.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
	|	И НаборыГрупп.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
	|	И НаборыГрупп.НовыйНаборГруппДоступа <> ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
	|	И &УточнениеПланаЗапроса
	|
	|СГРУППИРОВАТЬ ПО
	|	НаборыГрупп.Ссылка,
	|	НаборыГрупп.Пользователь,
	|	НаборыГрупп.НовыйНаборГруппДоступа
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	СтарыеНаборы.НаборИзОдногоПользователя КАК НаборИзОдногоПользователя,
	|	НовыеНаборы.ЧастьНомера1 КАК ЧастьНомера1
	|ПОМЕСТИТЬ НаборыГруппДляОбновления
	|ИЗ
	|	СтарыеНаборыГрупп КАК СтарыеНаборы
	|		ЛЕВОЕ СОЕДИНЕНИЕ НовыеНаборыГрупп КАК НовыеНаборы
	|		ПО (НовыеНаборы.Пользователь = СтарыеНаборы.Пользователь)
	|ГДЕ
	|	(НовыеНаборы.Пользователь ЕСТЬ NULL
	|				И СтарыеНаборы.ТекущийНаборГрупп <> ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
	|			ИЛИ СтарыеНаборы.ЧастьНомера1 <> ЕСТЬNULL(НовыеНаборы.ЧастьНомера1, -1234567))
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НаборыГрупп.Ссылка КАК НаборГрупп,
	|	СУММА(ЕСТЬNULL(НомераГрупп.ЧастьНомера1, -1234567)) КАК ЧастьНомера1
	|ПОМЕСТИТЬ СуществующиеНаборыГрупп
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГрупп
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыНаборов
	|		ПО (НаборыГрупп.ДляВнешнихПользователей = &ДляВнешнихПользователей)
	|			И (НаборыГрупп.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка))
	|			И (ГруппыНаборов.Ссылка = НаборыГрупп.Ссылка)
	|		ЛЕВОЕ СОЕДИНЕНИЕ НомераГрупп КАК НомераГрупп
	|		ПО (ГруппыНаборов.Группа = НомераГрупп.Группа)
	|			И (&УточнениеПланаЗапроса)
	|
	|СГРУППИРОВАТЬ ПО
	|	НаборыГрупп.Ссылка
	|
	|ИМЕЮЩИЕ
	|	СУММА(ЕСТЬNULL(НомераГрупп.ЧастьНомера1, -1234567)) >= 0
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НовыеНаборы.НаборИзОдногоПользователя КАК ТекущаяСсылка,
	|	ВЫБОР
	|		КОГДА НовыеНаборы.ЧастьНомера1 ЕСТЬ NULL
	|			ТОГДА ЗНАЧЕНИЕ(Справочник.НаборыГруппДоступа.ПустаяСсылка)
	|		ИНАЧЕ ЕСТЬNULL(СуществующиеНаборы.НаборГрупп, НЕОПРЕДЕЛЕНО)
	|	КОНЕЦ КАК НаборГрупп,
	|	ИСТИНА В
	|		(ВЫБРАТЬ ПЕРВЫЕ 1
	|			ИСТИНА
	|		ИЗ
	|			Справочник.НаборыГруппДоступа КАК ВсеНаборы
	|		ГДЕ
	|			ВсеНаборы.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|			И ВсеНаборы.НовыйНаборГруппДоступа = СуществующиеНаборы.НаборГрупп) КАК НаборГруппНовый,
	|	НовыеНаборы.ЧастьНомера1 КАК ЧастьНомера1
	|ИЗ
	|	НаборыГруппДляОбновления КАК НовыеНаборы
	|		ЛЕВОЕ СОЕДИНЕНИЕ СуществующиеНаборыГрупп КАК СуществующиеНаборы
	|		ПО (СуществующиеНаборы.ЧастьНомера1 = НовыеНаборы.ЧастьНомера1)
	|
	|УПОРЯДОЧИТЬ ПО
	|	ТекущаяСсылка,
	|	НаборГрупп";
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"ВЫБРАТЬ
		|	НЕОПРЕДЕЛЕНО КАК Пользователь,
		|	СУММА(НомераГрупп.ЧастьНомера1) КАК ЧастьНомера1
		|ПОМЕСТИТЬ НовыеНаборыГрупп
		|ИЗ
		|	НомераГрупп КАК НомераГрупп",
		ТекстЗапросаНовыхНаборовГрупп);
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "Справочник.Пользователи", "Справочник.ВнешниеПользователи");
	КонецЕсли;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка)", // @query-part-1
		"ЗНАЧЕНИЕ(Справочник." + ИмяСправочникаГрупп + ".ПустаяСсылка)"); // @query-part-1
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "НовыйНаборГруппДоступа",       "Новый"       + ИмяПоляНабора);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "РазрешенныйНаборГруппДоступа", "Разрешенный" + ИмяПоляНабора);
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"	НомераГрупп.ЧастьНомера1 КАК ЧастьНомера1", // @query-part-1
		"	НомераГрупп." + СтрСоединить(ИменаЧастейНомера, ",
		|	НомераГрупп."));
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"	СУММА(ЕСТЬNULL(НомераГрупп.ЧастьНомера1, -1234567)) >= 0", // @query-part-1 @query-part-2 @query-part-3
		"	СУММА(ЕСТЬNULL(НомераГрупп." + СтрСоединить(ИменаЧастейНомера, ", -1234567)) >= 0
		|	И СУММА(ЕСТЬNULL(НомераГрупп.") + ", -1234567)) >= 0");
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"	СУММА(ЕСТЬNULL(НомераГрупп.ЧастьНомера1, -1234567))
		|", // @query-part-1 @query-part-2 @query-part-3
		"	СУММА(ЕСТЬNULL(НомераГрупп." + СтрСоединить(ИменаЧастейНомера, ", -1234567)),
		|	СУММА(ЕСТЬNULL(НомераГрупп.") + ", -1234567))
		|");
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"	НовыеНаборы.ЧастьНомера1 КАК ЧастьНомера1", // @query-part-1
		"	НовыеНаборы." + СтрСоединить(ИменаЧастейНомера, ",
		|	НовыеНаборы."));
	
	УсловиеОтбора = "";
	УсловиеСоединения = "";
	ПоляСуммированияСтарых = "";
	ПоляСуммированияНовых = "";
	Для Каждого ИмяЧастиНомера Из ИменаЧастейНомера Цикл
		УсловиеОтбора = УсловиеОтбора + ?(УсловиеОтбора = "", "","
		|			ИЛИ ") + "СтарыеНаборы." + ИмяЧастиНомера + " <> ЕСТЬNULL(НовыеНаборы." + ИмяЧастиНомера + ", -1234567)"; // @query-part-1 @query-part-3
		УсловиеСоединения = УсловиеСоединения + ?(УсловиеСоединения = "", "", "
		|			И ") + "(СуществующиеНаборы." + ИмяЧастиНомера + " = НовыеНаборы." + ИмяЧастиНомера + ")"; // @query-part-1
		ПоляСуммированияСтарых = ПоляСуммированияСтарых + ?(ПоляСуммированияСтарых = "", "", ",
		|	") + "СУММА(ЕСТЬNULL(НомераГрупп." + ИмяЧастиНомера + ", -1234567)) КАК " + ИмяЧастиНомера; // @query-part-2 @query-part-3
		ПоляСуммированияНовых = ПоляСуммированияНовых + ?(ПоляСуммированияНовых = "", "", ",
		|	") + "СУММА(НомераГрупп." + ИмяЧастиНомера + ") КАК " + ИмяЧастиНомера; // @query-part-2 @query-part-3
	КонецЦикла;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"СтарыеНаборы.ЧастьНомера1 <> ЕСТЬNULL(НовыеНаборы.ЧастьНомера1, -1234567)", УсловиеОтбора); // @query-part-1
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"(СуществующиеНаборы.ЧастьНомера1 = НовыеНаборы.ЧастьНомера1)", УсловиеСоединения); // @query-part-1
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"СУММА(ЕСТЬNULL(НомераГрупп.ЧастьНомера1, -1234567)) КАК ЧастьНомера1", ПоляСуммированияСтарых); // @query-part-1
	Запрос.Текст = СтрЗаменить(Запрос.Текст,
		"СУММА(НомераГрупп.ЧастьНомера1) КАК ЧастьНомера1", ПоляСуммированияНовых); // @query-part-1
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "-1234567", Формат(-МаксимальныйНомерГруппыВЧастиНомера*2, "ЧГ="));
	
	УстановитьУточнениеПланаЗапроса(Запрос.Текст);
	
	Результат = Запрос.Выполнить().Выгрузить();
	
	СписокИменЧастейНабора = СтрСоединить(ИменаЧастейНомера, ", ");
	НовыеНаборыГрупп = Результат.Скопировать(Новый Массив, СписокИменЧастейНабора);
	НовыеНаборыГрупп.Колонки.Добавить("ГруппыНабора");
	НовыеНаборыГрупп.Колонки.Добавить("ИдентификаторГрупп");
	Отбор = Новый Структура(СписокИменЧастейНабора);
	
	ЭлементыДанных = Результат.Скопировать(Новый Массив, "ТекущаяСсылка, НаборГрупп, НаборГруппНовый");
	ЭлементыДанных.Колонки.Добавить("ГруппыНабора");
	ЭлементыДанных.Колонки.Добавить("ИдентификаторГрупп");
	
	Для Каждого Строка Из Результат Цикл
		НоваяСтрока = ЭлементыДанных.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
		Если Строка.НаборГрупп <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ЗаполнитьЗначенияСвойств(Отбор, Строка);
		НайденныеСтроки = НовыеНаборыГрупп.НайтиСтроки(Отбор);
		Если НайденныеСтроки.Количество() = 0 Тогда
			СвойстваНовогоНабора = НовыеНаборыГрупп.Добавить();
			ЗаполнитьЗначенияСвойств(СвойстваНовогоНабора, Строка);
			СвойстваНовогоНабора.ИдентификаторГрупп = Новый УникальныйИдентификатор;
			СвойстваНовогоНабора.ГруппыНабора =
				ГруппыНабораПоНомерам(НомераГрупп, ИменаЧастейНомера, СвойстваНовогоНабора);
		Иначе
			СвойстваНовогоНабора = НайденныеСтроки[0];
		КонецЕсли;
		НоваяСтрока.ГруппыНабора       = СвойстваНовогоНабора.ГруппыНабора;
		НоваяСтрока.ИдентификаторГрупп = СвойстваНовогоНабора.ИдентификаторГрупп;
	КонецЦикла;
	
	Возврат ЭлементыДанных;
	
КонецФункции

// Для процедуры ОбновитьПорциюЭлементов.
Процедура ОбновитьНаборыГруппНазначенныеПользователямВСправочнике(ЭлементыДанных,
			ПараметрыОбновления, ЭтоОбновлениеНазначенныхНаборовГруппДоступа)
	
	ДляВнешнихПользователей = ПараметрыОбновления.ДляВнешнихПользователей;
	
	Если ЭтоОбновлениеНазначенныхНаборовГруппДоступа Тогда
		ИмяПоляНабора = "НаборГруппДоступа";
		ТипЭлементовНабора = Справочники.ГруппыДоступа.ПустаяСсылка();
		ПредставлениеЭлементовГрупп = НСтр("ru = 'Группы доступа'",
			ОбщегоНазначения.КодОсновногоЯзыка());
		
	Иначе
		ИмяПоляНабора = "НаборГруппПользователей";
		Если Не ДляВнешнихПользователей Тогда
			ТипЭлементовНабора = Справочники.ГруппыПользователей.ПустаяСсылка();
			ПредставлениеЭлементовГрупп = НСтр("ru = 'Группы пользователей'",
				ОбщегоНазначения.КодОсновногоЯзыка());
		Иначе
			ТипЭлементовНабора = Справочники.ГруппыВнешнихПользователей.ПустаяСсылка();
			ПредставлениеЭлементовГрупп = НСтр("ru = 'Группы внешних пользователей'",
				ОбщегоНазначения.КодОсновногоЯзыка());
		КонецЕсли;
	КонецЕсли;
	
	НовыеНаборыГрупп = Новый Соответствие;
	
	Для Каждого Строка Из ЭлементыДанных Цикл
		Если Строка.НаборГрупп = Неопределено Тогда
			НаборГрупп = НовыеНаборыГрупп.Получить(Строка.ИдентификаторГрупп);
			Если НаборГрупп = Неопределено Тогда
				// @skip-check query-in-loop - Порционная обработка данных
				Строка.НаборГрупп = НовыйНаборГрупп(Строка.ГруппыНабора,
					ПараметрыОбновления.ДляВнешнихПользователей,
					ТипЭлементовНабора,
					ИмяПоляНабора,
					ПредставлениеЭлементовГрупп,
					Строка.НаборГруппНовый);
				НовыеНаборыГрупп.Вставить(Строка.ИдентификаторГрупп, Строка.НаборГрупп);
			Иначе
				Строка.НаборГрупп = НаборГрупп;
				Строка.НаборГруппНовый = Истина;
			КонецЕсли;
		КонецЕсли;
		
		Блокировка = Новый БлокировкаДанных;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Строка.ТекущаяСсылка);
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			
			Объект = СлужебныйЭлемент(Неопределено, Строка.ТекущаяСсылка);
			Если Строка.НаборГруппНовый Тогда
				Объект["Новый" + ИмяПоляНабора] = Строка.НаборГрупп;
			Иначе
				Объект["Разрешенный" + ИмяПоляНабора] = Строка.НаборГрупп;
				Объект["Новый" + ИмяПоляНабора] = ПараметрыОбновления.ПустойНаборГруппДоступа;
			КонецЕсли;
			Объект.Записать();
			
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 1) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьНаборыГруппВСправочнике.
Процедура ЗаполнитьНомераГрупп(НомераГрупп, ИменаЧастейНомера, МаксимальныйНомерГруппыВЧастиНомера)
	
	КоличествоГрупп = НомераГрупп.Количество();
	КоличествоЧастейНомера = Цел(КоличествоГрупп / 64) + 1;
	ОписаниеТиповНомера = Новый ОписаниеТипов("Число",,,
		Новый КвалификаторыЧисла(31, 0, ДопустимыйЗнак.Неотрицательный));
	
	Для Счетчик = 1 По КоличествоЧастейНомера Цикл
		ИмяЧастиНомера = "ЧастьНомера" + Формат(Счетчик, "ЧРГ=");
		ИменаЧастейНомера.Добавить(ИмяЧастиНомера);
		НомераГрупп.Колонки.Добавить(ИмяЧастиНомера, ОписаниеТиповНомера);
	КонецЦикла;
	
	ТекущаяЧастьНомера = 1;
	ТекущееИмяЧастиНомера = ИменаЧастейНомера[0];
	ТекущийНомерГруппы = 1;

	Для Каждого Строка Из НомераГрупп Цикл
		Строка[ТекущееИмяЧастиНомера] = ТекущийНомерГруппы;
		ТекущийНомерГруппы = ТекущийНомерГруппы * 2;
		Если ТекущийНомерГруппы > МаксимальныйНомерГруппыВЧастиНомера Тогда
			ТекущийНомерГруппы = 1;
			ТекущееИмяЧастиНомера = ИменаЧастейНомера[ТекущаяЧастьНомера];
			ТекущаяЧастьНомера = ТекущаяЧастьНомера + 1;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьНаборыГруппВСправочнике.
Функция НовыйНаборГрупп(ГруппыНабора, ДляВнешнихПользователей, ТипЭлементовНабора,
			ИмяПоляНабора, ПредставлениеЭлементовГрупп, НаборГруппНовый)
	
	НоваяСсылка = Справочники.НаборыГруппДоступа.ПолучитьСсылку();
	Объект = СлужебныйЭлемент(Справочники.НаборыГруппДоступа);
	Объект.УстановитьСсылкуНового(НоваяСсылка);
	Объект.ДляВнешнихПользователей = ДляВнешнихПользователей;
	Объект.ТипЭлементовНабора = ТипЭлементовНабора;
	Объект.Наименование = Строка(НоваяСсылка.УникальныйИдентификатор()) + " (" + ПредставлениеЭлементовГрупп + ")";
	
	Для Каждого ГруппаНабора Из ГруппыНабора Цикл
		Объект.Группы.Добавить().Группа = ГруппаНабора;
	КонецЦикла;
	
	ЗаполнитьХешНабораГрупп(Объект);
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
	ЭлементБлокировки.УстановитьЗначение("ТипЭлементовНабора", Объект.ТипЭлементовНабора);
	ЭлементБлокировки.УстановитьЗначение("Хеш", Объект.Хеш);
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Если НаборГруппСуществует(Объект) Тогда
			Запрос = Новый Запрос;
			Запрос.УстановитьПараметр("ДляВнешнихПользователей", ДляВнешнихПользователей);
			Запрос.УстановитьПараметр("СуществующийНабор", Объект.Ссылка);
			Запрос.Текст =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	ИСТИНА КАК ЗначениеИстина
			|ИЗ
			|	Справочник.НаборыГруппДоступа КАК ВсеНаборы
			|ГДЕ
			|	ВсеНаборы.ДляВнешнихПользователей = &ДляВнешнихПользователей
			|	И ВсеНаборы.НовыйНаборГруппДоступа = &СуществующийНабор";
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "НовыйНаборГруппДоступа", "Новый" + ИмяПоляНабора);
			
			Если Не Запрос.Выполнить().Пустой() Тогда
				НаборГруппНовый = Истина;
			КонецЕсли;
		Иначе
			Объект["Новый" + ИмяПоляНабора] = НоваяСсылка;
			Объект.Записать();
			НаборГруппНовый = Истина;
		КонецЕсли;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Возврат Объект.Ссылка;
	
КонецФункции

// Для функции НовыйНаборГрупп и процедуры ЗаполнитьПустыеХешиНаборовГрупп.
Процедура ЗаполнитьХешНабораГрупп(Объект)
	
	Объект.Группы.Сортировать("Группа", Новый СравнениеЗначений);
	
	ДанныеДляХеша = Объект.Группы.ВыгрузитьКолонку("Группа");
	СтрокаДляХеша = СтрокаДанныхДляХеширования(ДанныеДляХеша);
	Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32);
	Хеширование.Добавить(СтрокаДляХеша);
	Объект.Хеш = Хеширование.ХешСумма;
	
КонецПроцедуры

// Для функции НовыйНаборГрупп.
Функция ГруппыНабораПоНомерам(НомераГрупп, ИменаЧастейНомера, ЧастиНомера)
	
	ГруппыНабора = Новый Массив;
	ИндексГруппы = 0;
	Делитель = 4294967296; // 2^32.
	
	Для Каждого ИмяЧастиНомера Из ИменаЧастейНомера Цикл
		ЧастьНомера = ЧастиНомера[ИмяЧастиНомера];
		Для НомерБлокаЧастиНомера = 1 По 2 Цикл
			Целое = Цел(ЧастьНомера / Делитель);
			БлокЧастиНомера = ЧастьНомера - Целое * Делитель;
			ЧастьНомера = Целое;
			Если БлокЧастиНомера > 0 Тогда
				Для НомерБита = 0 По 31 Цикл
					Если ПроверитьБит(БлокЧастиНомера, НомерБита) Тогда
						ГруппыНабора.Добавить(НомераГрупп[ИндексГруппы + НомерБита].СсылкаГруппы);
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			ИндексГруппы = ИндексГруппы + 32;
		КонецЦикла;
	КонецЦикла;
	
	Возврат ГруппыНабора;
	
КонецФункции

// Для функции НовыйНаборГрупп.
Функция НаборГруппСуществует(Объект)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ТипЭлементовНабора", Объект.ТипЭлементовНабора);
	Запрос.УстановитьПараметр("Хеш", Объект.Хеш);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	СуществующиеНаборы.Ссылка КАК СсылкаНабора,
	|	СуществующиеНаборы.Группы.(
	|		Группы.Группа КАК Группа
	|	) КАК ГруппыНабора
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК СуществующиеНаборы
	|ГДЕ
	|	СуществующиеНаборы.ТипЭлементовНабора = &ТипЭлементовНабора
	|	И СуществующиеНаборы.Хеш = &Хеш
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка";
	
	ГруппыОбъекта = Объект.Группы.Выгрузить(, "Группа");
	ГруппыОбъекта.Индексы.Добавить("Группа");
	
	Выгрузка = Запрос.Выполнить().Выгрузить();
	Для Каждого Строка Из Выгрузка Цикл
		Если ГруппыОбъекта.Количество() <> Строка.ГруппыНабора.Количество() Тогда
			Продолжить;
		КонецЕсли;
		ГруппыСовпадают = Истина;
		Для Каждого Подстрока Из Строка.ГруппыНабора Цикл
			Если ГруппыОбъекта.Найти(Подстрока.Группа, "Группа") = Неопределено Тогда
				ГруппыСовпадают = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если ГруппыСовпадают Тогда
			Объект = Новый Структура("Ссылка", Строка.СсылкаНабора);
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для процедуры ОбновитьНаборыГруппДоступа.
Процедура ОбновитьНаборыГруппРазрешенныеПользователямВСправочнике(ЭлементыДанных, ПараметрыОбновления)
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
	
	Для Каждого Строка Из ЭлементыДанных Цикл
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Строка.ТекущаяСсылка);
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			
			Объект = СлужебныйЭлемент(Неопределено, Строка.ТекущаяСсылка);
			Если Объект <> Неопределено Тогда
				Если ЗначениеЗаполнено(Объект.НовыйНаборГруппДоступа) Тогда
					Объект.РазрешенныйНаборГруппДоступа = Объект.НовыйНаборГруппДоступа;
					Объект.НовыйНаборГруппДоступа = ПараметрыОбновления.ПустойНаборГруппДоступа;
				КонецЕсли;
				Если ЗначениеЗаполнено(Объект.НовыйНаборГруппПользователей) Тогда
					Объект.РазрешенныйНаборГруппПользователей = Объект.НовыйНаборГруппПользователей;
					Объект.НовыйНаборГруппПользователей = ПараметрыОбновления.ПустойНаборГруппДоступа;
				КонецЕсли;
				Если Объект.Модифицированность() Тогда
					Объект.Записать();
				КонецЕсли;
			КонецЕсли;
			
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 1) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура ОчиститьПраваНесуществующихНаборовГруппДоступа(ПараметрыОбновления)
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	КлючиДоступаНаборов.НаборГруппДоступа КАК Набор
	|ИЗ
	|	РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК КлючиДоступаНаборов
	|ГДЕ
	|	КлючиДоступаНаборов.НаборГруппДоступа.Ссылка ЕСТЬ NULL
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	КлючиДоступаНаборов.Пользователь КАК Набор
	|ИЗ
	|	РегистрСведений.КлючиДоступаПользователей КАК КлючиДоступаНаборов
	|ГДЕ
	|	КлючиДоступаНаборов.Пользователь.Ссылка ЕСТЬ NULL
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	КлючиДоступаНаборов.ВнешнийПользователь КАК Набор
	|ИЗ
	|	РегистрСведений.КлючиДоступаВнешнихПользователей КАК КлючиДоступаНаборов
	|ГДЕ
	|	КлючиДоступаНаборов.ВнешнийПользователь.Ссылка ЕСТЬ NULL";
	
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	
	Выборка = РезультатыЗапроса[0].Выбрать();
	Пока Выборка.Следующий() Цикл
		// @skip-check query-in-loop - Порционная обработка данных
		УдалитьЗаписиРегистраДляНабора(Выборка.Набор,
			"КлючиДоступаНаборовГруппДоступа", "НаборГруппДоступа");
	КонецЦикла;
	
	Выборка = РезультатыЗапроса[1].Выбрать();
	Пока Выборка.Следующий() Цикл
		// @skip-check query-in-loop - Порционная обработка данных
		УдалитьЗаписиРегистраДляНабора(Выборка.Набор,
			"КлючиДоступаПользователей", "Пользователь");
	КонецЦикла;
	
	Выборка = РезультатыЗапроса[2].Выбрать();
	Пока Выборка.Следующий() Цикл
		// @skip-check query-in-loop - Порционная обработка данных
		УдалитьЗаписиРегистраДляНабора(Выборка.Набор,
			"КлючиДоступаВнешнихПользователей", "ВнешнийПользователь");
	КонецЦикла;
	
КонецПроцедуры

// Для функции НаборыГруппДоступаДляОбновления.
Функция УстаревшиеНаборыГруппДоступаВСправочнике(ПараметрыОбновления, КоличествоВЗапросе)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ПараметрыОбновления.ДляВнешнихПользователей);
	Запрос.УстановитьПараметр("ДатаУстаревания", ДатаУстаревания());
	Запрос.УстановитьПараметр("РазрешенныйПустойНабор",
		УправлениеДоступомСлужебныйПовтИсп.РазрешенныйПустойНаборГруппДоступа());
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НаборыГруппДоступа.Ссылка КАК ТекущаяСсылка,
	|	НаборыГруппДоступа.ТипЭлементовНабора КАК ТипЭлементовНабора,
	|	ЛОЖЬ КАК Используется,
	|	ИСТИНА КАК Удалить
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
	|ГДЕ
	|	НаборыГруппДоступа.ТипЭлементовНабора = НЕОПРЕДЕЛЕНО
	|	И &УточнениеПланаЗапроса
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ
	|	НаборыГруппДоступа.Ссылка,
	|	НаборыГруппДоступа.ТипЭлементовНабора,
	|	ЛОЖЬ,
	|	ИСТИНА
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
	|ГДЕ
	|	НаборыГруппДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И НаборыГруппДоступа.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
	|	И (ВЫРАЗИТЬ(НаборыГруппДоступа.Пользователь КАК Справочник.Пользователи).Ссылка ЕСТЬ NULL
	|			ИЛИ НаборыГруппДоступа.НеИспользуетсяС <> ДАТАВРЕМЯ(1, 1, 1)
	|				И НаборыГруппДоступа.НеИспользуетсяС < &ДатаУстаревания)
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	НаборыГруппДоступа.Ссылка,
	|	НаборыГруппДоступа.ТипЭлементовНабора,
	|	НЕ МИНИМУМ(НаборыИзОдногоПользователя.Ссылка ЕСТЬ NULL),
	|	МИНИМУМ(НаборыГруппДоступа.НеИспользуетсяС <> ДАТАВРЕМЯ(1, 1, 1)
	|			И НаборыГруппДоступа.НеИспользуетсяС < &ДатаУстаревания)
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа КАК НаборыИзОдногоПользователя
	|		ПО (НаборыИзОдногоПользователя.ДляВнешнихПользователей = &ДляВнешнихПользователей)
	|			И (НаборыИзОдногоПользователя.РазрешенныйНаборГруппДоступа = НаборыГруппДоступа.Ссылка
	|				ИЛИ НаборыИзОдногоПользователя.НовыйНаборГруппДоступа = НаборыГруппДоступа.Ссылка)
	|ГДЕ
	|	НаборыГруппДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И НаборыГруппДоступа.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка)
	|	И НаборыГруппДоступа.Ссылка <> &РазрешенныйПустойНабор
	|
	|СГРУППИРОВАТЬ ПО
	|	НаборыГруппДоступа.Ссылка,
	|	НаборыГруппДоступа.ТипЭлементовНабора
	|
	|ИМЕЮЩИЕ
	|	(НаборыГруппДоступа.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
	|			И МИНИМУМ(НаборыИзОдногоПользователя.Ссылка ЕСТЬ NULL)
	|		ИЛИ НаборыГруппДоступа.НеИспользуетсяС <> ДАТАВРЕМЯ(1, 1, 1)
	|			И (НЕ МИНИМУМ(НаборыИзОдногоПользователя.Ссылка ЕСТЬ NULL)
	|				ИЛИ НаборыГруппДоступа.НеИспользуетсяС < &ДатаУстаревания))
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	НаборыГруппПользователей.Ссылка,
	|	НаборыГруппПользователей.ТипЭлементовНабора,
	|	НЕ МИНИМУМ(НаборыИзОдногоПользователя.Ссылка ЕСТЬ NULL),
	|	МИНИМУМ(НаборыГруппПользователей.НеИспользуетсяС <> ДАТАВРЕМЯ(1, 1, 1)
	|			И НаборыГруппПользователей.НеИспользуетсяС < &ДатаУстаревания)
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГруппПользователей
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа КАК НаборыИзОдногоПользователя
	|		ПО (НаборыИзОдногоПользователя.ДляВнешнихПользователей = &ДляВнешнихПользователей)
	|			И (НаборыИзОдногоПользователя.РазрешенныйНаборГруппПользователей = НаборыГруппПользователей.Ссылка
	|				ИЛИ НаборыИзОдногоПользователя.НовыйНаборГруппПользователей = НаборыГруппПользователей.Ссылка)
	|ГДЕ
	|	НаборыГруппПользователей.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И НаборыГруппПользователей.ТипЭлементовНабора = ЗНАЧЕНИЕ(Справочник.ГруппыПользователей.ПустаяСсылка)
	|
	|СГРУППИРОВАТЬ ПО
	|	НаборыГруппПользователей.Ссылка,
	|	НаборыГруппПользователей.ТипЭлементовНабора
	|
	|ИМЕЮЩИЕ
	|	(НаборыГруппПользователей.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
	|			И МИНИМУМ(НаборыИзОдногоПользователя.Ссылка ЕСТЬ NULL)
	|		ИЛИ НаборыГруппПользователей.НеИспользуетсяС <> ДАТАВРЕМЯ(1, 1, 1)
	|			И (НЕ МИНИМУМ(НаборыИзОдногоПользователя.Ссылка ЕСТЬ NULL)
	|				ИЛИ НаборыГруппПользователей.НеИспользуетсяС < &ДатаУстаревания))";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "Справочник.Пользователи", "Справочник.ВнешниеПользователи");
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
	КонецЕсли;
	УстановитьУточнениеПланаЗапроса(Запрос.Текст);
	
	ЭлементыДанных = Запрос.Выполнить().Выгрузить();
	
	КоличествоВЗапросе = ЭлементыДанных.Количество();
	Возврат ЭлементыДанных;
	
КонецФункции

// Для функций УстаревшиеНаборыГруппДоступаВСправочнике, ЭлементыДляОбновления.
Функция ДатаУстаревания()
	
	Возврат ТекущаяДатаСеанса() - КоличествоЧасовУстареванияНеиспользуемыхЭлементов() * 60 * 60;
	
КонецФункции

// Для процедуры ОбновитьПорциюЭлементов.
Процедура ОбработатьУстаревшиеНаборыВСправочнике(ЭлементыДанных, ПараметрыОбновления)
	
	Блокировка = Новый БлокировкаДанных;
	ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
	
	Для Каждого Строка Из ЭлементыДанных Цикл
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Строка.ТекущаяСсылка);
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			Объект = СлужебныйЭлемент(Неопределено, Строка.ТекущаяСсылка);
			Если Объект <> Неопределено Тогда
				Если Строка.Используется Тогда
					Объект.НеИспользуетсяС = '00010101';
				ИначеЕсли Не Строка.Удалить Тогда
					Объект.НеИспользуетсяС = ТекущаяДатаСеанса();
				Иначе
					Объект.ТипЭлементовНабора = Неопределено;
					Объект.Хеш = 0;
					Объект.Группы.Очистить();
				КонецЕсли;
				Объект.Записать();
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		Если Не Строка.Используется И Строка.Удалить Тогда
			// @skip-check query-in-loop - Порционная обработка данных
			УдалитьЗаписиРегистраДляНабора(Строка.ТекущаяСсылка, "КлючиДоступаНаборовГруппДоступа",  "НаборГруппДоступа");
			// @skip-check query-in-loop - Порционная обработка данных
			УдалитьЗаписиРегистраДляНабора(Строка.ТекущаяСсылка, "КлючиДоступаПользователей",        "Пользователь");
			// @skip-check query-in-loop - Порционная обработка данных
			УдалитьЗаписиРегистраДляНабора(Строка.ТекущаяСсылка, "КлючиДоступаВнешнихПользователей", "ВнешнийПользователь");
			
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				Объект = СлужебныйЭлемент(Неопределено, Строка.ТекущаяСсылка);
				Если Объект <> Неопределено Тогда
					Объект.Удалить();
				КонецЕсли;
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЕсли;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 1) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры УдалитьУстаревшиеНаборыВСправочнике.
Процедура УдалитьЗаписиРегистраДляНабора(Набор, ИмяРегистраСведений, ИмяПоляНабора)
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Набор", Набор);
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1000
	|	КлючиДоступаНаборов.КлючДоступа КАК КлючДоступа
	|ИЗ
	|	РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК КлючиДоступаНаборов
	|ГДЕ
	|	КлючиДоступаНаборов.НаборГруппДоступа = &Набор";
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "КлючиДоступаНаборовГруппДоступа", ИмяРегистраСведений);
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "НаборГруппДоступа", ИмяПоляНабора);
	
	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений[ИмяРегистраСведений]);
	ПолноеИмяРегистраСведений = "РегистрСведений." + ИмяРегистраСведений;
	
	Пока Истина Цикл
		Выгрузка = Запрос.Выполнить().Выгрузить();
		Если Выгрузка.Количество() = 0 Тогда
			Прервать;
		КонецЕсли;
		
		Блокировка = Новый БлокировкаДанных;
		Для Каждого Строка Из Выгрузка Цикл
			ЭлементБлокировки = Блокировка.Добавить(ПолноеИмяРегистраСведений);
			ЭлементБлокировки.УстановитьЗначение(ИмяПоляНабора, Набор);
			ЭлементБлокировки.УстановитьЗначение("КлючДоступа", Строка.КлючДоступа);
		КонецЦикла;
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			Для Каждого Строка Из Выгрузка Цикл
				ЭлементОтбора = НаборЗаписей.Отбор[ИмяПоляНабора]; // ЭлементОтбора
				ЭлементОтбора.Установить(Набор);
				НаборЗаписей.Отбор.КлючДоступа.Установить(Строка.КлючДоступа);
				НаборЗаписей.Записать();
			КонецЦикла;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура ОбновитьПраваНаРазрешенныйКлючДоступа(ЕстьИзменения = Ложь)
	
	ОбновитьГруппыДоступаРазрешенногоКлючаДоступа( , ЕстьИзменения);
	
КонецПроцедуры

// Для процедуры ОбновитьПраваНаРазрешенныйКлючДоступа.
Процедура ОбновитьГруппыДоступаРазрешенногоКлючаДоступа(ГруппыДоступа = Неопределено, ЕстьИзменения = Ложь) Экспорт
	
	УстановитьПривилегированныйРежим(Истина);
	РазрешенныйКлючДоступа = УправлениеДоступомСлужебныйПовтИсп.РазрешенныйКлючДоступа();
	РазрешенныйПустойНабор = УправлениеДоступомСлужебныйПовтИсп.РазрешенныйПустойНаборГруппДоступа();
	
	Блокировка = Новый БлокировкаДанных;
	
	// Обновление групп доступа в регистре КлючиДоступаГруппДоступа.
	ЗапросГрупп = Новый Запрос;
	ЗапросГрупп.УстановитьПараметр("КлючДоступа", РазрешенныйКлючДоступа);
	ЗапросГрупп.УстановитьПараметр("ПрофильАдминистратор", УправлениеДоступом.ПрофильАдминистратор());
	ЗапросГрупп.Текст = ТекстЗапросаВыбораРазличийГруппДоступаРазрешенногоКлюча();
	УстановитьУсловиеОтбораВЗапросе(ЗапросГрупп, ГруппыДоступа, "ГруппыДоступа",
		"&УсловиеОтбораГруппДоступа1:ГруппыДоступа.Ссылка
		|&УсловиеОтбораГруппДоступа2:СтарыеДанные.ГруппаДоступа"); // @query-part-1
	
	ЭлементБлокировкиГрупп = Блокировка.Добавить("РегистрСведений.КлючиДоступаГруппДоступа");
	ЭлементБлокировкиГрупп.УстановитьЗначение("КлючДоступа", РазрешенныйКлючДоступа);
	НаборЗаписейГрупп = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаГруппДоступа);
	НаборЗаписейГрупп.Отбор.КлючДоступа.Установить(РазрешенныйКлючДоступа);
	
	// Обновление наборов групп доступа в регистре КлючиДоступаНаборовГруппДоступа.
	ЗапросПравДляГруппДоступа = Новый Запрос;
	ЗапросПравДляГруппДоступа.УстановитьПараметр("КлючДоступа", РазрешенныйКлючДоступа);
	ЗапросПравДляГруппДоступа.УстановитьПараметр("РазрешенныйПустойНабор", РазрешенныйПустойНабор);
	ЗапросПравДляГруппДоступа.Текст = ТекстЗапросаВыбораРазличийПроизводныхПравДляГруппДоступа();
	
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаНаборовГруппДоступа");
	ЭлементБлокировки.УстановитьЗначение("КлючДоступа", РазрешенныйКлючДоступа);
	ПраваДляГруппДоступа = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаНаборовГруппДоступа);
	ПраваДляГруппДоступа.Отбор.КлючДоступа.Установить(РазрешенныйКлючДоступа);
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ПрофилиГруппДоступа");
		ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.ГруппыДоступа");
		ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
		ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		
		РезультатЗапросаГрупп = ЗапросГрупп.Выполнить();
		ОбновитьПроизводныеПраваНаКлючДоступа(РезультатЗапросаГрупп,
			НаборЗаписейГрупп, "ГруппаДоступа", РазрешенныйКлючДоступа, ЕстьИзменения);
		
		РезультатЗапросаПравДляГруппДоступа = ЗапросПравДляГруппДоступа.Выполнить();
		ОбновитьПроизводныеПраваНаКлючДоступа(РезультатЗапросаПравДляГруппДоступа,
			ПраваДляГруппДоступа, "НаборГруппДоступа", РазрешенныйКлючДоступа, ЕстьИзменения);
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ОбновитьГруппыРазрешенногоКлючаДоступа.
Функция ТекстЗапросаВыбораРазличийГруппДоступаРазрешенногоКлюча()
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ВсеСтроки.ГруппаДоступа КАК ГруппаДоступа,
	|	ВсеСтроки.ПравоИзменение КАК ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление КАК ПравоДобавление,
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки
	|ИЗ
	|	(ВЫБРАТЬ
	|		ГруппыДоступа.Ссылка КАК ГруппаДоступа,
	|		ИСТИНА КАК ПравоИзменение,
	|		ИСТИНА КАК ПравоДобавление,
	|		1 КАК ВидИзмененияСтроки
	|	ИЗ
	|		Справочник.ГруппыДоступа КАК ГруппыДоступа
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа КАК ПрофилиГруппДоступа
	|			ПО ГруппыДоступа.Профиль = ПрофилиГруппДоступа.Ссылка
	|				И (ГруппыДоступа.Профиль <> &ПрофильАдминистратор)
	|				И (НЕ ГруппыДоступа.ПометкаУдаления)
	|				И (НЕ ПрофилиГруппДоступа.ПометкаУдаления)
	|				И (&УсловиеОтбораГруппДоступа1)
	|				И (ИСТИНА В
	|					(ВЫБРАТЬ ПЕРВЫЕ 1
	|						ИСТИНА КАК ЗначениеИстина
	|					ИЗ
	|						Справочник.ГруппыДоступа.Пользователи КАК УчастникиГруппДоступа
	|					ГДЕ
	|						УчастникиГруппДоступа.Ссылка = ГруппыДоступа.Ссылка))
	|				И (&УточнениеПланаЗапроса)
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		СтарыеДанные.ГруппаДоступа,
	|		СтарыеДанные.ПравоИзменение,
	|		СтарыеДанные.ПравоДобавление,
	|		-1
	|	ИЗ
	|		РегистрСведений.КлючиДоступаГруппДоступа КАК СтарыеДанные
	|	ГДЕ
	|		СтарыеДанные.КлючДоступа = &КлючДоступа
	|		И &УсловиеОтбораГруппДоступа2) КАК ВсеСтроки
	|
	|СГРУППИРОВАТЬ ПО
	|	ВсеСтроки.ГруппаДоступа,
	|	ВсеСтроки.ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление
	|
	|ИМЕЮЩИЕ
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
	|
	|УПОРЯДОЧИТЬ ПО
	|	ВидИзмененияСтроки";
	
	УстановитьУточнениеПланаЗапроса(ТекстЗапроса);
	
	Возврат ТекстЗапроса;
	
КонецФункции

// Для процедуры ОбновитьПорциюЭлементов.
Процедура УдалитьУстаревшиеЭлементыДанныхСписка(ЭлементыДанных, ПараметрыОбновления)
	
	РазмерПорции = 100; // Удаление N элементов данных по 100 за раз.
	ПорцияЭлементовДанных = Неопределено;
	КоличествоЭлементов = ЭлементыДанных.Количество();
	
	Если ПараметрыОбновления.ЭтоСсылочныйТип Тогда
		НаборИзОднойЗаписи = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаКОбъектам);
		Индекс = 0;
		Пока Индекс < КоличествоЭлементов Цикл
			Если ПорцияЭлементовДанных = Неопределено Тогда
				ПорцияЭлементовДанных = ЭлементыДанных.Скопировать(Новый Массив);
				Блокировка = Новый БлокировкаДанных;
			КонецЕсли;
			ЭлементДанных = ЭлементыДанных[Индекс];
			ЗаполнитьЗначенияСвойств(ПорцияЭлементовДанных.Добавить(), ЭлементДанных);
			
			ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаКОбъектам");
			ЭлементБлокировки.УстановитьЗначение("Объект", ЭлементДанных.ТекущаяСсылка);
			
			Индекс = Индекс + 1;
			Если ПорцияЭлементовДанных.Количество() < РазмерПорции И Индекс < КоличествоЭлементов Тогда
				Продолжить;
			КонецЕсли;
			
			КоличествоОбработанных = ПорцияЭлементовДанных.Количество();
			БлокировкаУстановлена = Ложь;
			НачатьТранзакцию();
			Попытка
				Блокировка.Заблокировать();
				БлокировкаУстановлена = Истина;
				Для Каждого Строка Из ПорцияЭлементовДанных Цикл
					НаборИзОднойЗаписи.Отбор.Объект.Установить(Строка.ТекущаяСсылка);
					Если Не ПараметрыОбновления.ДляВнешнихПользователей
					   И (ПараметрыОбновления.БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей
					      Или Строка.Удалить) Тогда
						НаборИзОднойЗаписи.Очистить();
						НаборИзОднойЗаписи.Записать();
					Иначе
						НаборИзОднойЗаписи.Прочитать();
						Если НаборИзОднойЗаписи.Количество() > 0 Тогда
							Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
								НаборИзОднойЗаписи[0].КлючДоступаВнешнихПользователей = Неопределено;
							Иначе
								НаборИзОднойЗаписи[0].КлючДоступаПользователей = Неопределено;
							КонецЕсли;
							Если Не ЗначениеЗаполнено(НаборИзОднойЗаписи[0].КлючДоступаВнешнихПользователей)
							   И Не ЗначениеЗаполнено(НаборИзОднойЗаписи[0].КлючДоступаПользователей) Тогда
								НаборИзОднойЗаписи.Очистить();
							КонецЕсли;
							НаборИзОднойЗаписи.Записать();
						КонецЕсли;
					КонецЕсли;
				КонецЦикла;
				ЗафиксироватьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				Если БлокировкаУстановлена Тогда
					ВызватьИсключение;
				КонецЕсли;
			КонецПопытки;
			КоличествоОбработанных = 0;
			
			Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления) Тогда
				Прервать;
			КонецЕсли;
		КонецЦикла;
		
		Возврат;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей)
	   И ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных <> "УстаревшиеЭлементыОбщегоРегистра" Тогда
		
		ИмяРегистра = ПараметрыОбновления.ИмяОтдельногоРегистраКлючей;
	Иначе
		ИмяРегистра = "КлючиДоступаКРегистрам";
	КонецЕсли;
	НаборИзОднойЗаписи = СлужебныйНаборЗаписей(РегистрыСведений[ИмяРегистра]);
	
	Если ИмяРегистра = "КлючиДоступаКРегистрам" Тогда
		НаборИзОднойЗаписи.Отбор.Регистр.Установить(ПараметрыОбновления.ИдентификаторСписка);
	КонецЕсли;
	
	ИменаИзмерений = Новый Массив;
	ИменаИзмерений.Добавить("ВариантДоступа");
	Для Каждого Колонка Из ЭлементыДанных.Колонки Цикл
		Если СтрНачинаетсяС(Колонка.Имя, "Поле") Тогда
			ИменаИзмерений.Добавить(Колонка.Имя);
		КонецЕсли;
	КонецЦикла;
	ОтборИзмерений = Неопределено;
	
	Индекс = 0;
	Пока Индекс < КоличествоЭлементов Цикл
		Если ПорцияЭлементовДанных = Неопределено Тогда
			ТребуетсяПроверка = Ложь;
			ПорцияЭлементовДанных = ЭлементыДанных.Скопировать(Новый Массив);
			Блокировка = Новый БлокировкаДанных;
		КонецЕсли;
		ЭлементДанных = ЭлементыДанных[Индекс];
		ЗаполнитьЗначенияСвойств(ПорцияЭлементовДанных.Добавить(), ЭлементДанных);
		Если Не ПараметрыОбновления.БезЗаписиКлючейДоступа И Не ЭлементДанных.Удалить Тогда
			ТребуетсяПроверка = Истина;
		КонецЕсли;
		
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений." + ИмяРегистра);
		Если ИмяРегистра = "КлючиДоступаКРегистрам" Тогда
			ЭлементБлокировки.УстановитьЗначение("Регистр", ПараметрыОбновления.ИдентификаторСписка);
		КонецЕсли;
		Для Каждого ИмяИзмерения Из ИменаИзмерений Цикл
			ЭлементБлокировки.УстановитьЗначение(ИмяИзмерения, ЭлементДанных[ИмяИзмерения]);
		КонецЦикла;
		
		Индекс = Индекс + 1;
		Если ПорцияЭлементовДанных.Количество() < РазмерПорции И Индекс < КоличествоЭлементов Тогда
			Продолжить;
		КонецЕсли;
		
		Если ТребуетсяПроверка Тогда
			ЭлементБлокировки = Блокировка.Добавить(ПараметрыОбновления.Список);
			ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
			Запрос = Новый Запрос;
			Запрос.Текст = ПараметрыОбновления.ТекстЗапросаПроверкиУстаревшихЭлементовДанных;
			Запрос.УстановитьПараметр("КлючиДоступаКРегистрам", ПорцияЭлементовДанных);
			УстановитьУточнениеПланаЗапроса(Запрос.Текст);
		КонецЕсли;
		
		КоличествоОбработанных = ПорцияЭлементовДанных.Количество();
		БлокировкаУстановлена = Ложь;
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			БлокировкаУстановлена = Истина;
			Если ТребуетсяПроверка Тогда
				// @skip-check query-in-loop - Порционная обработка данных
				Выгрузка = Запрос.Выполнить().Выгрузить();
				Если ОтборИзмерений = Неопределено Тогда
					ИменаПроверяемыхИзмерений = Новый Массив;
					Для Каждого Колонка Из Выгрузка.Колонки Цикл
						ИменаПроверяемыхИзмерений.Добавить(Колонка.Имя);
					КонецЦикла;
					ОтборИзмерений = Новый Структура(СтрСоединить(ИменаПроверяемыхИзмерений, ","));
				КонецЕсли;
			КонецЕсли;
			Для Каждого Строка Из ПорцияЭлементовДанных Цикл
				Если ТребуетсяПроверка И Не Строка.Удалить Тогда
					ЗаполнитьЗначенияСвойств(ОтборИзмерений, Строка);
					Если Выгрузка.НайтиСтроки(ОтборИзмерений).Количество() = 0 Тогда
						Продолжить;
					КонецЕсли;
				КонецЕсли;
				Для Каждого ИмяИзмерения Из ИменаИзмерений Цикл
					ЭлементОтбора = НаборИзОднойЗаписи.Отбор[ИмяИзмерения]; // ЭлементОтбора
					Если Строка[ИмяИзмерения] = Неопределено Тогда
						ЭлементОтбора.Значение = Неопределено;
						ЭлементОтбора.Использование = Истина;
					Иначе
						ЭлементОтбора.Установить(Строка[ИмяИзмерения]);
					КонецЕсли;
				КонецЦикла;
				НаборИзОднойЗаписи.Записать();
			КонецЦикла;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			Если БлокировкаУстановлена Тогда
				ВызватьИсключение;
			КонецЕсли;
			КоличествоОбработанных = 0;
		КонецПопытки;
		ПорцияЭлементовДанных = Неопределено;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, КоличествоОбработанных) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ВыполнитьОбновлениеДоступаСписка.
Процедура УдалитьОбъектыНедопустимыхТиповВРегистреКлючиДоступаКОбъектам()
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ТИПЗНАЧЕНИЯ(КлючиДоступаКДанным.Объект) КАК ТипСсылки
	|ИЗ
	|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКДанным";
	
	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаКОбъектам);
	ДопустимыеТипы = УправлениеДоступомСлужебныйПовтИсп.ОписаниеТиповСсылокДопустимыхОбъектов();
	Выборка = Запрос.Выполнить().Выбрать();
	
	Запрос.Текст =
	"ВЫБРАТЬ
	|	КлючиДоступаКДанным.Объект КАК Объект
	|ИЗ
	|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКДанным
	|ГДЕ
	|	ТИПЗНАЧЕНИЯ(КлючиДоступаКДанным.Объект) = &Тип";
	
	Пока Выборка.Следующий() Цикл
		Если Выборка.ТипСсылки = Тип("Неопределено") Тогда
			НаборЗаписей.Отбор.Объект.Установить(Неопределено);
			НаборЗаписей.Записать();
			Продолжить;
		ИначеЕсли ДопустимыеТипы.СодержитТип(Выборка.ТипСсылки) Тогда
			Продолжить;
		КонецЕсли;
		Запрос.УстановитьПараметр("Тип", Выборка.ТипСсылки);
		// @skip-check query-in-loop - Порционная обработка данных
		Объекты = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Объект");
		Для Каждого Объект Из Объекты Цикл
			НаборЗаписей.Отбор.Объект.Установить(Объект);
			НаборЗаписей.Записать();
		КонецЦикла;
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
	|ГДЕ
	|	КлючиДоступаКРегистрам.Регистр = НЕОПРЕДЕЛЕНО";
	
	Если Запрос.Выполнить().Пустой() Тогда
		Возврат;
	КонецЕсли;
	
	НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаКРегистрам);
	НаборЗаписей.Отбор.Регистр.Установить(Неопределено);
	НаборЗаписей.Записать();
	
КонецПроцедуры

// Для процедуры ОбновитьПорциюЭлементов.
Процедура ОбновитьЭлементыДанныхСпискаСУстаревшимиКлючами(ЭлементыДанных, ПараметрыОбновления)
	
	РазмерПорции = 100; // Загрузка N элементов данных по 100 за раз.
	
	ЭтоОбработкаСуществующихКомбинаций = Не ПараметрыОбновления.ЭтоСсылочныйТип
		И ПараметрыОбновления.ПоследнийОбновленныйЭлемент.ВидКлючаДанных = "ЭлементыСУстаревшимиКлючами";
	
	Индекс = 0;
	Пока Индекс < ЭлементыДанных.Количество() Цикл
		
		ПорцияЭлементовДанных = ЭлементыДанных.Скопировать(Новый Массив);
		Если Не ПараметрыОбновления.ЭтоСсылочныйТип Тогда
			ПорцияЭлементовДанных.Колонки.Добавить("ТекущаяСсылка", Новый ОписаниеТипов("Число"));
			ПорцияУдаляемыхЭлементовДанных = Новый Массив;
		КонецЕсли;
		
		Пока Индекс < ЭлементыДанных.Количество()
		   И ПорцияЭлементовДанных.Количество() < РазмерПорции
		   И (ПараметрыОбновления.ЭтоСсылочныйТип
		      Или ПорцияУдаляемыхЭлементовДанных.Количество() < РазмерПорции) Цикл
			
			ЭлементДанных = ЭлементыДанных[Индекс];
			
			Если ЭтоОбработкаСуществующихКомбинаций
			   И НекорректнаяКомбинацияЗначенийОпорныхПолей(ЭлементДанных, ПараметрыОбновления) Тогда
				
				Если ПорцияЭлементовДанных.Количество() > 0 Тогда
					Прервать;
				КонецЕсли;
				ПорцияУдаляемыхЭлементовДанных.Добавить(ЭлементДанных);
				
			ИначеЕсли ЭтоОбработкаСуществующихКомбинаций
			        И ПорцияУдаляемыхЭлементовДанных.Количество() > 0 Тогда
				Прервать;
			Иначе
				НоваяСтрока = ПорцияЭлементовДанных.Добавить();
				ЗаполнитьЗначенияСвойств(НоваяСтрока, ЭлементДанных);
				Если Не ПараметрыОбновления.ЭтоСсылочныйТип Тогда
					НоваяСтрока.ТекущаяСсылка = ПорцияЭлементовДанных.Индекс(НоваяСтрока) + 1;
				КонецЕсли;
			КонецЕсли;
			Индекс = Индекс + 1;
		КонецЦикла;
		
		Если ЭтоОбработкаСуществующихКомбинаций
		   И ПорцияУдаляемыхЭлементовДанных.Количество() > 0 Тогда
			
			УдалитьНекорректныеКомбинацииЗначенийОпорныхПолей(ПорцияУдаляемыхЭлементовДанных, ПараметрыОбновления);
			Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, ПорцияУдаляемыхЭлементовДанных.Количество()) Тогда
				Прервать;
			КонецЕсли;
		КонецЕсли;
		
		Если ПорцияЭлементовДанных.Количество() > 0 Тогда
			// @skip-check query-in-loop - Порционная обработка данных
			ОбновитьКлючиДоступаПорцииЭлементовДанныхСписка(ПорцияЭлементовДанных, ПараметрыОбновления);
		КонецЕсли;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 0) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для функции ОбновитьЭлементыДанныхСпискаСУстаревшимиКлючами.
Функция НекорректнаяКомбинацияЗначенийОпорныхПолей(ЭлементДанных, ПараметрыОбновления)
	
	Номер = 1;
	Для Каждого ХранилищеТиповПоля Из ПараметрыОбновления.ОпорныеПоля.ТипыИспользуемых Цикл
		ТипыПоля = ХранилищеТиповПоля.Получить();
		
		Если Не ТипыПоля.СодержитТип(ТипЗнч(ЭлементДанных["Поле" + Номер]))
		   И ЭлементДанных["Поле" + Номер] <> Неопределено Тогда
			
			Возврат Истина;
		КонецЕсли;
		
		Номер = Номер + 1;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для функции ДоступРазрешен.
Функция МодельОбъектовВПамяти(ОписаниеДанных, ПараметрыОграничения)
	
	Если ТипЗнч(ОписаниеДанных) = Тип("Массив") Тогда
		Объекты = ОписаниеДанных;
	Иначе
		Объекты = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ОписаниеДанных);
	КонецЕсли;
	Объект = Объекты[0]; // СправочникОбъект
	
	ЭлементыДанных = Новый ТаблицаЗначений;
	ЭлементыДанных.Колонки.Добавить("ТекущаяСсылка", Новый ОписаниеТипов(
		ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ТипЗнч(Объект.Ссылка))));
	
	Модель = Новый Структура;
	Модель.Вставить("ЭлементыДанных", ЭлементыДанных);
	Модель.Вставить("Таблицы", Новый Соответствие);
	Модель.Вставить("КлючиДоступаКОбъектам",
		РегистрыСведений.КлючиДоступаКОбъектам.СоздатьНаборЗаписей().Выгрузить());
	
	ПоляТаблицОбъекта = ПараметрыОграничения.ПоляТаблицОбъекта;
	
	Для Каждого Объект Из Объекты Цикл
		Объект = Объект; // СправочникОбъект
		ТекущаяСсылка = ПользователиСлужебный.СсылкаОбъекта(ОписаниеДанных);
		ЭлементыДанных.Добавить().ТекущаяСсылка = ТекущаяСсылка;
		Для Каждого ОписаниеТаблицы Из ПоляТаблицОбъекта Цикл
			ОписаниеТаблицы = ОписаниеТаблицы; // см. НовыеПоляТаблицыОбъекта
			Таблица = Модель.Таблицы.Получить(ОписаниеТаблицы.ПолноеИмяТаблицы); // ТаблицаЗначений
			СписокПолей = ОписаниеТаблицы.СписокПолей;
			Если Таблица = Неопределено Тогда
				Таблица = ОписаниеТаблицы.ТаблицаСПолями.Получить();
				Модель.Таблицы.Вставить(ОписаниеТаблицы.ПолноеИмяТаблицы, Таблица);
			КонецЕсли;
			Если ЗначениеЗаполнено(ОписаниеТаблицы.ТабличнаяЧасть) Тогда
				Выгрузка = Объект[ОписаниеТаблицы.ТабличнаяЧасть].Выгрузить(, СписокПолей); // ТаблицаЗначений
				Выгрузка.Свернуть(СписокПолей);
				Для Каждого Строка Из Выгрузка Цикл
					НоваяСтрока = Таблица.Добавить();
					ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка, СписокПолей);
					НоваяСтрока.Ссылка = ТекущаяСсылка;
				КонецЦикла;
			Иначе
				НоваяСтрока = Таблица.Добавить();
				ЗаполнитьЗначенияСвойств(НоваяСтрока, Объект, СписокПолей);
				НоваяСтрока.Ссылка = ТекущаяСсылка;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Возврат Модель;
	
КонецФункции

// Для функции ОбновитьЭлементыДанныхСпискаСУстаревшимиКлючами, ОбновитьКлючиДоступаЭлементовДанныхПриЗаписи.
Процедура ОбновитьКлючиДоступаПорцииЭлементовДанныхСписка(ПорцияЭлементовДанных, ПараметрыОбновления)
	
	ЭтоСсылочныйТип     = ПараметрыОбновления.ЭтоСсылочныйТип;
	ИдентификаторСписка = ПараметрыОбновления.ИдентификаторСписка;
	
	Контекст = Новый Структура;
	Контекст.Вставить("ПорцияЭлементовДанных", ПорцияЭлементовДанных);
	
	ЗапросЗначенийЭлементовДанных = Новый Запрос;
	Если ЭтоСсылочныйТип Тогда
		Контекст.Вставить("СсылкиНаОбъекты", ПорцияЭлементовДанных.ВыгрузитьКолонку("ТекущаяСсылка"));
	КонецЕсли;
	Если ЗначениеЗаполнено(ПараметрыОбновления.СоставПолей) Тогда
		Если ЭтоСсылочныйТип Тогда
			ЗапросЗначенийЭлементовДанных.УстановитьПараметр("СсылкиНаОбъекты", Контекст.СсылкиНаОбъекты);
			ИндексТаблицы = 0;
		КонецЕсли;
		Если ПараметрыОбновления.Свойство("МодельОбъектовВПамяти") Тогда
			ЗапросЗначенийЭлементовДанных.Текст = ПараметрыОбновления.ТекстЗапросаЗначенийОбъектовВПамятиДляКлючейДоступа;
			Для Каждого Таблица Из ПараметрыОбновления.МодельОбъектовВПамяти.Таблицы Цикл
				ЗапросЗначенийЭлементовДанных.УстановитьПараметр(Таблица.Ключ, Таблица.Значение);
			КонецЦикла;
			ИндексТаблицы = ПараметрыОбновления.МодельОбъектовВПамяти.Таблицы.Количество();
		Иначе
			ЗапросЗначенийЭлементовДанных.Текст = ПараметрыОбновления.ТекстЗапросаЗначенийЭлементовДанныхДляКлючейДоступа;
			Если Не ЭтоСсылочныйТип Тогда
				ЗапросЗначенийЭлементовДанных.УстановитьПараметр("ИдентификаторРегистра", ИдентификаторСписка);
				ЗапросЗначенийЭлементовДанных.УстановитьПараметр("ЗначенияОпорныхПолей",  ПорцияЭлементовДанных);
				ИндексТаблицы = 1;
			КонецЕсли;
		КонецЕсли;
		УстановитьУточнениеПланаЗапроса(ЗапросЗначенийЭлементовДанных.Текст);
		РезультатыЗапросаЗначенийЭлементов = ЗапросЗначенийЭлементовДанных.ВыполнитьПакет();
	Иначе
		РезультатыЗапросаЗначенийЭлементов = Новый Массив;
	КонецЕсли;
	
	ДанныеСтроковыхКлючейДоступа = Новый Соответствие;
	ТаблицыКлюча = ПараметрыОбновления.ТаблицыКлюча;
	
	ЗначенияСтрокТаблиц = Новый Соответствие;
	КлючиЗначенийСтрокОбъектов = КлючиЗначенийСтрокОбъектов(РезультатыЗапросаЗначенийЭлементов,
		ИндексТаблицы, ТаблицыКлюча, ЗначенияСтрокТаблиц);
	
	ТребуемыеКлючиДоступа         = Новый Массив;
	ХешиТребуемыхКлючейДоступа    = Новый Массив;
	ОписаниеКлючейДоступаОбъектов = Новый Массив;
	Для Каждого ЭлементДанных Из ПорцияЭлементовДанных Цикл
		ОписаниеКлючейЗначений = КлючиЗначенийСтрокОбъектов.Получить(ЭлементДанных.ТекущаяСсылка);
		СтрокаДляХеша = СтрокаДляХешаКлючаДоступа(ОписаниеКлючейЗначений, ТаблицыКлюча);
		Свойства = ДанныеСтроковыхКлючейДоступа.Получить(СтрокаДляХеша);
		Если Свойства = Неопределено Тогда
			Свойства = Новый Структура("ЗначенияТаблиц, СтрокаДляХеша, Хеш, КлючДоступа, ЗначенияКолонокТаблиц");
			ДанныеСтроковыхКлючейДоступа.Вставить(СтрокаДляХеша, Свойства);
			ЗначенияТаблиц = Новый Массив;
			Для Каждого ИмяТаблицы Из ТаблицыКлюча Цикл
				КлючЗначений = ОписаниеКлючейЗначений.КлючиЗначений[ТаблицыКлюча.Найти(ИмяТаблицы)];
				ЗначенияСтрок = ЗначенияСтрокТаблиц.Получить(ИмяТаблицы).Получить(КлючЗначений);
				Если ЗначенияСтрок = Неопределено Тогда
					ЗначенияСтрок = Новый Массив;
				КонецЕсли;
				ЗначенияТаблиц.Добавить(Новый Структура("ИмяТаблицы, Таблица", ИмяТаблицы, ЗначенияСтрок));
			КонецЦикла;
			Свойства.ЗначенияТаблиц = ЗначенияТаблиц;
			Свойства.СтрокаДляХеша = СтрокаДляХеша;
			Свойства.ЗначенияКолонокТаблиц = ОписаниеКлючейЗначений.ЗначенияКолонокТаблиц;
			Хеширование = Новый ХешированиеДанных(ХешФункция.CRC32);
			Хеширование.Добавить(СтрокаДляХеша);
			Свойства.Хеш = Хеширование.ХешСумма;
			ТребуемыеКлючиДоступа.Добавить(Свойства);
			ХешиТребуемыхКлючейДоступа.Добавить(Свойства.Хеш);
		КонецЕсли;
		ОписаниеКлючейДоступаОбъектов.Добавить(
			Новый Структура("ТекущаяСсылка, СвойстваКлюча", ЭлементДанных.ТекущаяСсылка, Свойства));
	КонецЦикла;
	Контекст.Вставить("ОписаниеКлючейДоступаОбъектов", ОписаниеКлючейДоступаОбъектов);
	
	// Получение данных существующих ключей доступа по хешам требуемых ключей доступа.
	ЗапросЗначенийКлючей = Новый Запрос;
	ЗапросЗначенийКлючей.Текст = ПараметрыОбновления.ТекстЗапросаЗначенийИзИспользуемыхКлючейДоступаДляСравнения;
	ЗапросЗначенийКлючей.УстановитьПараметр("Хеши",   ХешиТребуемыхКлючейДоступа);
	ЗапросЗначенийКлючей.УстановитьПараметр("Список", ИдентификаторСписка);
	УстановитьУточнениеПланаЗапроса(ЗапросЗначенийКлючей.Текст);
	РезультатыЗапросаЗначенийКлючей = ЗапросЗначенийКлючей.ВыполнитьПакет();
	
	КлючиЗначенийСтрокКлючей = КлючиЗначенийСтрокОбъектов(РезультатыЗапросаЗначенийКлючей,
		?(Не ЗначениеЗаполнено(ПараметрыОбновления.СоставПолей)
			Или СтрНачинаетсяС(ТаблицыКлюча[0], "Шапка"), 0, 1),
		ТаблицыКлюча);
	Выборка = РезультатыЗапросаЗначенийКлючей[0].Выбрать();
	
	Пока Выборка.Следующий() Цикл
		ОписаниеКлючейЗначений = КлючиЗначенийСтрокКлючей.Получить(Выборка.ТекущаяСсылка);
		СтрокаДляХеша = СтрокаДляХешаКлючаДоступа(ОписаниеКлючейЗначений, ТаблицыКлюча);
		Свойства = ДанныеСтроковыхКлючейДоступа.Получить(СтрокаДляХеша);
		Если Свойства <> Неопределено И Свойства.КлючДоступа = Неопределено Тогда
			Свойства.КлючДоступа = Выборка.ТекущаяСсылка;
		КонецЕсли;
	КонецЦикла;
	
	// Создание недостающих ключей доступа.
	ОписаниеНовыхКлючей = Новый Массив;
	Для Каждого ОписаниеКлюча Из ТребуемыеКлючиДоступа Цикл
		Если ОписаниеКлюча.КлючДоступа <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ОписаниеНовыхКлючей.Добавить(ОписаниеКлюча);
	КонецЦикла;
	Если ОписаниеНовыхКлючей.Количество() > 0 Тогда
		ОбновитьПраваНаКлючиДоступа(ОписаниеНовыхКлючей, ПараметрыОбновления, Истина, Контекст);
		Для Каждого ОписаниеКлюча Из ОписаниеНовыхКлючей Цикл
			КлючДоступаОбъект = ОписаниеКлюча.КлючДоступаОбъект; // СправочникОбъект.КлючиДоступа
			Если ЗначениеЗаполнено(КлючДоступаОбъект.Ссылка) Тогда
				ОписаниеКлюча.КлючДоступа = КлючДоступаОбъект.Ссылка;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	// Обновление ключей доступа элементов данных.
	Если ЭтоСсылочныйТип Тогда
		Если ПараметрыОбновления.Свойство("МодельОбъектовВПамяти") Тогда
			ИмяПоляКлюча = ?(ПараметрыОбновления.ДляВнешнихПользователей,
				"КлючДоступаВнешнихПользователей", "КлючДоступаПользователей");
			КлючиДоступаКОбъектам = ПараметрыОбновления.МодельОбъектовВПамяти.КлючиДоступаКОбъектам; // РегистрСведенийНаборЗаписей
			Для Каждого ОписаниеКлючаДоступаОбъекта Из ОписаниеКлючейДоступаОбъектов Цикл
				НоваяСтрока = КлючиДоступаКОбъектам.Добавить();
				НоваяСтрока.Объект = ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка;
				НоваяСтрока[ИмяПоляКлюча] = ОписаниеКлючаДоступаОбъекта.СвойстваКлюча.КлючДоступа;
			КонецЦикла;
		Иначе
			ЗаписатьКлючиДоступаОбъектов(ПараметрыОбновления, Контекст);
		КонецЕсли;
	Иначе
		ЗаписатьКлючиДоступаРегистров(ПараметрыОбновления, Контекст);
	КонецЕсли;
	
	// Принудительное обновление прав вручную.
	Если ПараметрыОбновления.Свойство("ОбновитьПраваНаКлючи")
	   И ПараметрыОбновления.ОбновитьПраваНаКлючи Тогда
		
		СуществующиеКлючиДоступа = Новый Массив;
		Для Каждого ОписаниеКлюча Из ТребуемыеКлючиДоступа Цикл
			Если ОписаниеНовыхКлючей.Найти(ОписаниеКлюча) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			СуществующиеКлючиДоступа.Добавить(ОписаниеКлюча.КлючДоступа);
		КонецЦикла;
		Если СуществующиеКлючиДоступа.Количество() > 0 Тогда
			ОбновитьПраваНаКлючиДоступа(СуществующиеКлючиДоступа, ПараметрыОбновления);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбновитьКлючиДоступаПорцииЭлементовДанныхСписка.
Процедура ОбновитьПраваНаКлючиДоступа(ОписаниеКлючей, ПараметрыОбновления, ЭтоНовыеКлючи = Ложь, Контекст = Неопределено)
	
	Если ПараметрыОбновления.Свойство("КоличествоОбработанныхЭлементов") Тогда
		КоличествоОбработанных = ПараметрыОбновления.КоличествоОбработанныхЭлементов;
		ПараметрыОбновления.КоличествоОбработанныхЭлементов = 0;
	КонецЕсли;
	
	Если ЭтоНовыеКлючи Тогда
		КлючиДоступа = Новый ТаблицаЗначений;
		КлючиДоступа.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.КлючиДоступа"));
		
		ЗапросЗначенийКлючей = Новый Запрос;
		ЗапросЗначенийКлючей.Текст = ПараметрыОбновления.ТекстЗапросаЗначенийИзВсехКлючейДоступаДляСравнения;
		ЗапросЗначенийКлючей.УстановитьПараметр("Список", ПараметрыОбновления.ИдентификаторСписка);
		УстановитьУточнениеПланаЗапроса(ЗапросЗначенийКлючей.Текст);
		
		ЗапросСуществованияКлючей = Новый Запрос;
		ЗапросСуществованияКлючей.Текст = ПараметрыОбновления.ТекстЗапросаСуществованияКлючейДляСравнения;
		ЗапросСуществованияКлючей.УстановитьПараметр("Список", ПараметрыОбновления.ИдентификаторСписка);
		УстановитьУточнениеПланаЗапроса(ЗапросСуществованияКлючей.Текст);
		
		ОписаниеНовыхКлючей = Новый Структура;
		ОписаниеНовыхКлючей.Вставить("ОписаниеКлючей",            ОписаниеКлючей);
		ОписаниеНовыхКлючей.Вставить("КлючиДоступа",              КлючиДоступа);
		ОписаниеНовыхКлючей.Вставить("ЗначенияТаблиц",            Новый Структура);
		ОписаниеНовыхКлючей.Вставить("ОписанияКлючейПоСсылке",    Новый Соответствие);
		ОписаниеНовыхКлючей.Вставить("ЗапросЗначенийКлючей",      ЗапросЗначенийКлючей);
		ОписаниеНовыхКлючей.Вставить("ЗапросСуществованияКлючей", ЗапросСуществованияКлючей);
		
		ДопустимыеТипыЗначений = УправлениеДоступомСлужебныйПовтИсп.ДопустимыеТипыЗначенийКлючейДоступа();
		ЗначенияТаблиц = ОписаниеНовыхКлючей.ЗначенияТаблиц;
		
		Для Каждого ТаблицаКлюча Из ПараметрыОбновления.ТаблицыКлюча Цикл
			ТаблицаЗначений = ЗначенияТаблицыКлюча();
			ТаблицаЗначений.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.КлючиДоступа"));
			ПоляТаблицы = ПараметрыОбновления.РеквизитыТаблицКлюча.Получить(ТаблицаКлюча);
			Если СтрНачинаетсяС(ТаблицаКлюча, "Шапка") И ТаблицаКлюча <> "Шапка0" Тогда
				ТаблицаЗначений.Колонки.Добавить("НомерСтроки", Новый ОписаниеТипов("Число"));
			КонецЕсли;
			Для Каждого Поле Из ПоляТаблицы Цикл
				ТаблицаЗначений.Колонки.Добавить(Поле, ДопустимыеТипыЗначений);
			КонецЦикла;
			ЗначенияТаблиц.Вставить(ТаблицаКлюча, ТаблицаЗначений);
		КонецЦикла;
		
		Для Каждого ОписаниеКлюча Из ОписаниеКлючей Цикл
			ПроверитьТипЗначенийКлючаДоступа(ОписаниеКлюча, ДопустимыеТипыЗначений, ПараметрыОбновления);
			ПодготовитьНовыйКлючДоступа(ОписаниеКлюча, ОписаниеНовыхКлючей, ПараметрыОбновления);
		КонецЦикла;
		ОписаниеКлючейДоступа = ОписаниеНовыхКлючей;
	Иначе
		ОписаниеКлючейДоступа = Новый ТаблицаЗначений;
		ОписаниеКлючейДоступа.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.КлючиДоступа"));
		Для Каждого КлючДоступаСсылка Из ОписаниеКлючей Цикл
			ОписаниеКлючейДоступа.Добавить().Ссылка = КлючДоступаСсылка;
		КонецЦикла;
	КонецЕсли;
	
	ОбновитьПраваПорцииКлючейДоступаСписка(ОписаниеКлючейДоступа, ПараметрыОбновления, ЭтоНовыеКлючи);
	
	Если ПараметрыОбновления.Свойство("КоличествоОбработанныхЭлементов") Тогда
		ПараметрыОбновления.КоличествоОбработанныхЭлементов = КоличествоОбработанных;
	КонецЕсли;
	
КонецПроцедуры

// Возвращаемое значение:
//  ТаблицаЗначений:
//    * Ссылка      - СправочникСсылка.КлючиДоступа
//    * НомерСтроки - Число - номер строки табличной части
//
Функция ЗначенияТаблицыКлюча()
	
	Возврат Новый ТаблицаЗначений;
	
КонецФункции

// Для процедуры ОбновитьКлючиДоступаПорцииЭлементовДанныхСписка.
Функция СтрокаДляХешаКлючаДоступа(ОписаниеКлючейЗначений, ТаблицыКлюча)
	
	Если ОписаниеКлючейЗначений = Неопределено Тогда
		ОписаниеКлючейЗначений = Новый Структура(
			"КлючиЗначений, ИменаТаблиц, ЗначенияКолонокТаблиц",
			Новый Массив, Новый Массив, Новый Массив);
	КонецЕсли;
	
	КлючиЗначений = ОписаниеКлючейЗначений.КлючиЗначений;
	ИменаТаблиц   = ОписаниеКлючейЗначений.ИменаТаблиц;
	
	Если ИменаТаблиц.Количество() <> ТаблицыКлюча.Количество() Тогда
		// В ключе доступа используются табличные части и некоторые пустые.
		Для Индекс = 0 По ТаблицыКлюча.Количество() - 1 Цикл
			
			Если Индекс >= ИменаТаблиц.Количество()
			 Или ИменаТаблиц[Индекс] <> ТаблицыКлюча[Индекс] Тогда
				
				ИменаТаблиц.Вставить(Индекс, ТаблицыКлюча[Индекс]);
				КлючиЗначений.Вставить(Индекс, "6ab8db6a-4878-483a-b9d5-ef905ff1537e");
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат СтрСоединить(КлючиЗначений);
	
КонецФункции

// Для процедуры ОбновитьКлючиДоступаПорцииЭлементовДанных.
Процедура ЗаписатьКлючиДоступаОбъектов(ПараметрыОбновления, Контекст)
	
	ЗаписыватьТолькоИзмененные = ЗаписыватьТолькоИзмененныеКлючиДоступаЭлементовДанных();
	
	Если ПараметрыОбновления.СЗаписьюКлючейДоступаДляПользователейИВнешнихПользователей
	 Или ЗаписыватьТолькоИзмененные Тогда
		
		ЗапросТекущихКлючей = Новый Запрос;
		ЗапросТекущихКлючей.Текст =
		"ВЫБРАТЬ
		|	КлючиДоступаКОбъектам.Объект КАК Объект,
		|	КлючиДоступаКОбъектам.КлючДоступаПользователей КАК КлючДоступаПользователей,
		|	КлючиДоступаКОбъектам.КлючДоступаВнешнихПользователей КАК КлючДоступаВнешнихПользователей
		|ИЗ
		|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
		|ГДЕ
		|	КлючиДоступаКОбъектам.Объект В (&СсылкиНаОбъекты)";
		ЗапросТекущихКлючей.УстановитьПараметр("СсылкиНаОбъекты", Контекст.СсылкиНаОбъекты);
	КонецЕсли;
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		ИмяРеквизитаСохраняемогоКлюча = "КлючДоступаПользователей";
		ИмяРеквизитаОбновляемогоКлюча = "КлючДоступаВнешнихПользователей";
	Иначе
		ИмяРеквизитаСохраняемогоКлюча = "КлючДоступаВнешнихПользователей";
		ИмяРеквизитаОбновляемогоКлюча = "КлючДоступаПользователей";
	КонецЕсли;
	
	НаборИзОднойЗаписи = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаКОбъектам);
	Запись = НаборИзОднойЗаписи.Добавить();
	ОбъектТип = Метаданные.РегистрыСведений.КлючиДоступаКОбъектам.Измерения.Объект.Тип;
	Если Не ОбъектТип.СодержитТип(ТипЗнч(Контекст.ОписаниеКлючейДоступаОбъектов[0].ТекущаяСсылка)) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Невозможно обновить ключ доступа объекта ""%1"" типа ""%2"",
			           |так как этот тип не указан в определяемом типе %3.'"),
			Строка(Контекст.ОписаниеКлючейДоступаОбъектов[0].ТекущаяСсылка),
			Строка(ТипЗнч(Контекст.ОписаниеКлючейДоступаОбъектов[0].ТекущаяСсылка)),
			"ВладелецЗначенийКлючейДоступа");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если ЗаписыватьТолькоИзмененные Тогда
		ПередЗапросомТекущихКлючейДоступа(ПараметрыОбновления);
		ТекущиеКлючиДоБлокировки = ЗапросТекущихКлючей.Выполнить().Выгрузить();
		ПослеЗапросаТекущихКлючейДоступа(ПараметрыОбновления);
	КонецЕсли;
	
	КоличествоОбработанных = 0;
	ОписаниеКлючейДоступаОбъектов = Новый Массив;
	
	Блокировка = Новый БлокировкаДанных;
	Для Каждого ОписаниеКлючаДоступаОбъекта Из Контекст.ОписаниеКлючейДоступаОбъектов Цикл
		КлючДоступа = ОписаниеКлючаДоступаОбъекта.СвойстваКлюча.КлючДоступа;
		Если КлючДоступа = Неопределено Тогда
			Прервать;
		КонецЕсли;
		Если ЗаписыватьТолькоИзмененные Тогда
			Строка = ТекущиеКлючиДоБлокировки.Найти(ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка, "Объект");
			Если Строка <> Неопределено И Строка[ИмяРеквизитаОбновляемогоКлюча] = КлючДоступа Тогда
				КоличествоОбработанных = КоличествоОбработанных + 1;
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		ОписаниеКлючейДоступаОбъектов.Добавить(ОписаниеКлючаДоступаОбъекта);
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаКОбъектам");
		ЭлементБлокировки.УстановитьЗначение("Объект", ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка);
	КонецЦикла;
	
	Если ОписаниеКлючейДоступаОбъектов.Количество() = 0 Тогда
		ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, КоличествоОбработанных);
		Возврат;
	КонецЕсли;
	
	СпискиДляОбновления = Новый Структура("ИменаСписков, ДляВнешнихПользователей",
		ПараметрыОбновления.ЗависимыеСпискиПоКлючамДоступа,
		ПараметрыОбновления.ДляВнешнихПользователей);
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		ЗапланироватьОбновлениеУстаревшихКлючейДоступа(СпискиДляОбновления,
			ПараметрыОбновления.ИдентификаторТранзакции, "ЗаполнитьКэшПараметровОграниченияДоступа");
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		ПередБлокировкойДанных(ПараметрыОбновления);
		Блокировка.Заблокировать();
		Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
			ЗаблокироватьРегистрыПланированияОбновленияКлючейДоступаВФайловойИБ();
		КонецЕсли;
		ПослеБлокировкиДанных(ПараметрыОбновления);
		
		Если ПараметрыОбновления.СЗаписьюКлючейДоступаДляПользователейИВнешнихПользователей Тогда
			ПередЗапросомТекущихКлючейДоступа(ПараметрыОбновления);
			ТекущиеКлючи = ЗапросТекущихКлючей.Выполнить().Выгрузить();
			ПослеЗапросаТекущихКлючейДоступа(ПараметрыОбновления);
		КонецЕсли;
		
		ПередЗаписьюСтрок(ПараметрыОбновления);
		СсылкиНаОбъекты = Новый Массив;
		Для Каждого ОписаниеКлючаДоступаОбъекта Из ОписаниеКлючейДоступаОбъектов Цикл
			СсылкиНаОбъекты.Добавить(ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка);
			НаборИзОднойЗаписи.Отбор.Объект.Установить(ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка);
			Запись.Объект = ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка;
			Запись[ИмяРеквизитаОбновляемогоКлюча] = ОписаниеКлючаДоступаОбъекта.СвойстваКлюча.КлючДоступа;
			
			Если ПараметрыОбновления.СЗаписьюКлючейДоступаДляПользователейИВнешнихПользователей Тогда
				Строка = ТекущиеКлючи.Найти(ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка, "Объект");
				Если Строка <> Неопределено Тогда
					Запись[ИмяРеквизитаСохраняемогоКлюча] = Строка[ИмяРеквизитаСохраняемогоКлюча];
				КонецЕсли;
			КонецЕсли;
			НаборИзОднойЗаписи.Записать();
		КонецЦикла;
		ПослеЗаписиСтрок(ПараметрыОбновления, ОписаниеКлючейДоступаОбъектов.Количество());
		
		ПередПланированиемОбновления(ПараметрыОбновления);
		ЗапланироватьОбновлениеУстаревшихКлючейДоступа(СпискиДляОбновления,
			ПараметрыОбновления.ИдентификаторТранзакции,
			"ЗаписатьКлючиДоступаОбъектов",
			?(СсылкиНаОбъекты.Количество() > 25, Неопределено,
				Новый Структура("ПоКлючамДоступа", СсылкиНаОбъекты)),
			ПараметрыОбновления.Свойство("ЭтоФоновоеОбновлениеДоступа"));
		ПослеПланированияОбновления(ПараметрыОбновления);
		
		// АПК:330-выкл - №783.1.3 Допустимо указать вызов после ЗафиксироватьТранзакцию,
		// так как это вызов пустой процедуры в штатном режиме (то есть исключение невозможно),
		// а в режиме анализа производительности последствия учитываются и не являются критичными.
		ПередФиксациейТранзакции(ПараметрыОбновления);
		ЗафиксироватьТранзакцию();
		ПослеФиксацииТранзакции(ПараметрыОбновления);
		// АПК:330-вкл.
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	КоличествоОбработанных = КоличествоОбработанных + ОписаниеКлючейДоступаОбъектов.Количество();
	Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, КоличествоОбработанных) Тогда
		Возврат;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбновитьКлючиДоступаПорцииЭлементовДанных.
Процедура ЗаписатьКлючиДоступаРегистров(ПараметрыОбновления, Контекст)
	
	Если Не ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей) Тогда
		ИмяРегистраКлючей = "КлючиДоступаКРегистрам";
	Иначе
		ИмяРегистраКлючей = ПараметрыОбновления.ИмяОтдельногоРегистраКлючей;
	КонецЕсли;
	НаборИзОднойЗаписи = СлужебныйНаборЗаписей(РегистрыСведений[ИмяРегистраКлючей]);
	Запись = НаборИзОднойЗаписи.Добавить();
	
	ПорцияЭлементовДанных = Контекст.ПорцияЭлементовДанных;
	ПустыеЗначенияОпорныхПолей = УправлениеДоступомСлужебныйПовтИсп.ПустыеЗначенияОпорныхПолей(
		ПараметрыОбновления.ОпорныеПоля.МаксимальноеКоличество);
	
	ЗаписыватьТолькоИзмененные = ЗаписыватьТолькоИзмененныеКлючиДоступаЭлементовДанных();
	Если ЗаписыватьТолькоИзмененные Тогда
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("ИдентификаторРегистра", ПараметрыОбновления.ИдентификаторСписка);
		ТекстЗапроса = ПараметрыОбновления.ТекстЗапросаТекущихКлючейДоступаРегистра;
		ТекстыПакета = Новый Массив;
		НомерСтроки = 1;
		Для Каждого ОписаниеКлючаДоступаОбъекта Из Контекст.ОписаниеКлючейДоступаОбъектов Цикл
			ТекущийНомер = "_" + Формат(НомерСтроки, "ЧГ=");
			ТекстыПакета.Добавить(СтрЗаменить(ТекстЗапроса, "_%1", ТекущийНомер));
			Для НомерПоля = 1 По ПараметрыОбновления.ОпорныеПоля.Используемые.Количество() Цикл
				ИмяПоля = "Поле" + НомерПоля;
				ЭлементДанных = ПорцияЭлементовДанных.Найти(ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка, "ТекущаяСсылка");
				Запрос.УстановитьПараметр(ИмяПоля + ТекущийНомер, ЭлементДанных[ИмяПоля]);
			КонецЦикла;
			НомерСтроки = НомерСтроки + 1;
		КонецЦикла;
		Запрос.Текст = СтрСоединить(ТекстыПакета, ОбщегоНазначения.РазделительПакетаЗапросов());
		ПередЗапросомТекущихКлючейДоступа(ПараметрыОбновления);
		Если ТекстыПакета.Количество() > 1 Тогда
			РезультатыЗапроса = Запрос.ВыполнитьПакет();
		Иначе
			РезультатыЗапроса = Новый Массив;
			РезультатыЗапроса.Добавить(Запрос.Выполнить());
		КонецЕсли;
		ПослеЗапросаТекущихКлючейДоступа(ПараметрыОбновления);
	КонецЕсли;
	
	КоличествоОбработанных = 0;
	ОписаниеКлючейДоступаОбъектов = Новый Массив;
	
	ИндексРезультата = -1;
	Блокировка = Новый БлокировкаДанных;
	Для Каждого ОписаниеКлючаДоступаОбъекта Из Контекст.ОписаниеКлючейДоступаОбъектов Цикл
		ИндексРезультата = ИндексРезультата + 1;
		КлючДоступа = ОписаниеКлючаДоступаОбъекта.СвойстваКлюча.КлючДоступа;
		Если КлючДоступа = Неопределено Тогда
			Прервать;
		КонецЕсли;
		Если ЗаписыватьТолькоИзмененные Тогда
			РезультатЗапроса = РезультатыЗапроса[ИндексРезультата];
			Если Не РезультатЗапроса.Пустой() Тогда
				Выгрузка = РезультатЗапроса.Выгрузить();
				Если Выгрузка.Количество() = 1 И Выгрузка[0].КлючДоступа = КлючДоступа Тогда
					КоличествоОбработанных = КоличествоОбработанных + 1;
					Продолжить;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		ОписаниеКлючейДоступаОбъектов.Добавить(ОписаниеКлючаДоступаОбъекта);
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений." + ИмяРегистраКлючей);
		Если Не ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей) Тогда
			ЭлементБлокировки.УстановитьЗначение("Регистр", ПараметрыОбновления.ИдентификаторСписка);
		КонецЕсли;
		ЭлементБлокировки.УстановитьЗначение("ВариантДоступа", ПараметрыОбновления.ВариантДоступа);
		ЭлементДанных = ПорцияЭлементовДанных.Найти(ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка, "ТекущаяСсылка");
		Для НомерПоля = 1 По ПараметрыОбновления.ОпорныеПоля.Используемые.Количество() Цикл
			ИмяПоля = "Поле" + НомерПоля;
			ЭлементБлокировки.УстановитьЗначение(ИмяПоля, ЭлементДанных[ИмяПоля]);
		КонецЦикла;
	КонецЦикла;
	
	Если ОписаниеКлючейДоступаОбъектов.Количество() = 0 Тогда
		ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, КоличествоОбработанных);
		Возврат;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		ПередБлокировкойДанных(ПараметрыОбновления);
		Блокировка.Заблокировать();
		ПослеБлокировкиДанных(ПараметрыОбновления);
		
		ПередЗаписьюСтрок(ПараметрыОбновления);
		Для Каждого ОписаниеКлючаДоступаОбъекта Из ОписаниеКлючейДоступаОбъектов Цикл
			Если Не ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей) Тогда
				НаборИзОднойЗаписи.Отбор.Регистр.Установить(ПараметрыОбновления.ИдентификаторСписка);
				Запись.Регистр = ПараметрыОбновления.ИдентификаторСписка;
			КонецЕсли;
			НаборИзОднойЗаписи.Отбор.ВариантДоступа.Установить(ПараметрыОбновления.ВариантДоступа);
			Запись.ВариантДоступа = ПараметрыОбновления.ВариантДоступа;
			
			ЭлементДанных = ПорцияЭлементовДанных.Найти(ОписаниеКлючаДоступаОбъекта.ТекущаяСсылка, "ТекущаяСсылка");
			ЗаполнитьЗначенияСвойств(Запись, ПустыеЗначенияОпорныхПолей);
			Для НомерПоля = 1 По ПараметрыОбновления.ОпорныеПоля.Используемые.Количество() Цикл
				ИмяПоля = "Поле" + НомерПоля;
				ЭлементОтбора = НаборИзОднойЗаписи.Отбор[ИмяПоля]; // ЭлементОтбора
				Если ЭлементДанных[ИмяПоля] = Неопределено Тогда
					ЭлементОтбора.Значение = Неопределено;
					ЭлементОтбора.Использование = Истина;
				Иначе
					ЭлементОтбора.Установить(ЭлементДанных[ИмяПоля]);
				КонецЕсли;
				Запись[ИмяПоля] = ЭлементДанных[ИмяПоля];
			КонецЦикла;
			
			Запись.КлючДоступа = ОписаниеКлючаДоступаОбъекта.СвойстваКлюча.КлючДоступа;
			НаборИзОднойЗаписи.Записать();
		КонецЦикла;
		ПослеЗаписиСтрок(ПараметрыОбновления, ОписаниеКлючейДоступаОбъектов.Количество());
		
		// АПК:330-выкл - №783.1.3 Допустимо указать вызов после ЗафиксироватьТранзакцию,
		// так как это вызов пустой процедуры в штатном режиме (то есть исключение невозможно),
		// а в режиме анализа производительности последствия учитываются и не являются критичными.
		ПередФиксациейТранзакции(ПараметрыОбновления);
		ЗафиксироватьТранзакцию();
		ПослеФиксацииТранзакции(ПараметрыОбновления);
		// АПК:330-вкл.
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	КоличествоОбработанных = КоличествоОбработанных + ОписаниеКлючейДоступаОбъектов.Количество();
	Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, КоличествоОбработанных) Тогда
		Возврат;
	КонецЕсли;
	
КонецПроцедуры

// Для функции ОбновитьЭлементыДанныхСпискаСУстаревшимиКлючами.
Процедура УдалитьНекорректныеКомбинацииЗначенийОпорныхПолей(ПорцияЭлементовДанных, ПараметрыОбновления)
	
	Если Не ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей) Тогда
		ИмяРегистраКлючей = "КлючиДоступаКРегистрам";
	Иначе
		ИмяРегистраКлючей = ПараметрыОбновления.ИмяОтдельногоРегистраКлючей;
	КонецЕсли;
	НаборИзОднойЗаписи = СлужебныйНаборЗаписей(РегистрыСведений[ИмяРегистраКлючей]);
	КоличествоИспользуемыхПолей = ПараметрыОбновления.ОпорныеПоля.Используемые.Количество();
	
	Блокировка = Новый БлокировкаДанных;
	Для Каждого ЭлементДанных Из ПорцияЭлементовДанных Цикл
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений." + ИмяРегистраКлючей);
		Если Не ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей) Тогда
			ЭлементБлокировки.УстановитьЗначение("Регистр", ПараметрыОбновления.ИдентификаторСписка);
		КонецЕсли;
		ЭлементБлокировки.УстановитьЗначение("ВариантДоступа", ПараметрыОбновления.ВариантДоступа);
		Для НомерПоля = 1 По КоличествоИспользуемыхПолей Цикл
			ИмяПоля = "Поле" + НомерПоля;
			ЭлементБлокировки.УстановитьЗначение(ИмяПоля, ЭлементДанных[ИмяПоля]);
		КонецЦикла;
	КонецЦикла;
	
	НачатьТранзакцию();
	Попытка
		Блокировка.Заблокировать();
		Для Каждого ЭлементДанных Из ПорцияЭлементовДанных Цикл
			Если Не ЗначениеЗаполнено(ПараметрыОбновления.ИмяОтдельногоРегистраКлючей) Тогда
				НаборИзОднойЗаписи.Отбор.Регистр.Установить(ПараметрыОбновления.ИдентификаторСписка);
			КонецЕсли;
			НаборИзОднойЗаписи.Отбор.ВариантДоступа.Установить(ПараметрыОбновления.ВариантДоступа);
			Для НомерПоля = 1 По КоличествоИспользуемыхПолей Цикл
				ИмяПоля = "Поле" + НомерПоля;
				ЭлементОтбора = НаборИзОднойЗаписи.Отбор[ИмяПоля]; // ЭлементОтбора
				Если ЭлементДанных[ИмяПоля] = Неопределено Тогда
					ЭлементОтбора.Значение = Неопределено;
					ЭлементОтбора.Использование = Истина;
				Иначе
					ЭлементОтбора.Установить(ЭлементДанных[ИмяПоля]);
				КонецЕсли;
			КонецЦикла;
			НаборИзОднойЗаписи.Записать();
		КонецЦикла;
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ОбновитьКлючиДоступаПорцииЭлементовДанных.
Функция КлючиЗначенийСтрокОбъектов(РезультатыЗапроса, Индекс, ТаблицыКлюча, ЗначенияСтрокТаблиц = Неопределено)
	
	КлючиЗначенийСтрокОбъектов = Новый Соответствие;
	
	Если ЗначенияСтрокТаблиц = Неопределено Тогда
		ЗначенияСтрокТаблиц = Новый Соответствие;
	КонецЕсли;
	
	Для Каждого ИмяТаблицыКлючаДоступа Из ТаблицыКлюча Цикл
		ЗначенияСтрокТаблицы = ЗначенияСтрокТаблиц.Получить(ИмяТаблицыКлючаДоступа);
		Если ЗначенияСтрокТаблицы = Неопределено Тогда
			ЗначенияСтрокТаблицы = Новый Соответствие;
			ЗначенияСтрокТаблиц.Вставить(ИмяТаблицыКлючаДоступа, ЗначенияСтрокТаблицы);
		КонецЕсли;
		Дерево = РезультатыЗапроса[Индекс].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
		Индекс = Индекс + 1;
		Для Каждого Строка Из Дерево.Строки Цикл
			ЗначенияКолонокТаблицы = Новый Массив;
			Для Каждого Колонка Из Дерево.Колонки Цикл
				Если СтрНачинаетсяС(Колонка.Имя, "Значение") Тогда
					ЗначенияКолонокТаблицы.Добавить(Строка.Строки.ВыгрузитьКолонку(Колонка.Имя));
				КонецЕсли;
			КонецЦикла;
			КлючЗначенийСтрок = СтрокаДанныхДляХеширования(ЗначенияКолонокТаблицы);
			Если ЗначенияСтрокТаблицы.Получить(КлючЗначенийСтрок) = Неопределено Тогда
				ЗначенияСтрокТаблицы.Вставить(КлючЗначенийСтрок, Строка.Строки);
			КонецЕсли;
			ОписаниеКлючейЗначений = КлючиЗначенийСтрокОбъектов.Получить(Строка.ТекущаяСсылка);
			Если ОписаниеКлючейЗначений = Неопределено Тогда
				ОписаниеКлючейЗначений = Новый Структура(
					"КлючиЗначений, ИменаТаблиц, ЗначенияКолонокТаблиц",
					Новый Массив, Новый Массив, Новый Массив);
				КлючиЗначенийСтрокОбъектов.Вставить(Строка.ТекущаяСсылка, ОписаниеКлючейЗначений);
			КонецЕсли;
			ОписаниеКлючейЗначений.ИменаТаблиц.Добавить(ИмяТаблицыКлючаДоступа);
			ОписаниеКлючейЗначений.КлючиЗначений.Добавить(КлючЗначенийСтрок);
			ОписаниеКлючейЗначений.ЗначенияКолонокТаблиц.Добавить(ЗначенияКолонокТаблицы);
		КонецЦикла;
	КонецЦикла;
	
	Возврат КлючиЗначенийСтрокОбъектов;
	
КонецФункции

// Для функции КлючиЗначенийСтрокОбъектов и др.
Функция СтрокаДанныхДляХеширования(Данные)
	
	// Возвращает строку данных для последующего хеширования, например,
	// строковое описание ссылок, сохраняемых в базе данных, с учетом типов
	// по внутренним идентификаторам, что обеспечивает неизменность хеш-суммы
	// при изменении имен таблиц и имен реквизитов, то есть обеспечивает
	// соответствие хеш-суммы данных самим данным, сохраняемым в базе данных.
	//
	// Это позволяет избежать избыточного массового пересоздания ключей доступа с последующим
	// перерасчетом пользователей и групп доступа для пересозданных ключей доступа.
	
	Возврат ЗначениеВСтрокуВнутр(Данные);
	
КонецФункции

// Для процедуры ОбновитьКлючиДоступаПорцииЭлементовДанных.
Процедура ПроверитьТипЗначенийКлючаДоступа(ОписаниеКлюча, ДопустимыеТипыЗначений, ПараметрыОбновления)
	
	ЗначенияКолонокТаблиц = ОписаниеКлюча.ЗначенияКолонокТаблиц;
	
	Для Каждого ЗначенияКолонокТаблицы Из ЗначенияКолонокТаблиц Цикл
		Для Каждого ЗначенияКолонкиТаблицы Из ЗначенияКолонокТаблицы Цикл
			Для Каждого Значение Из ЗначенияКолонкиТаблицы Цикл
				Если Не ДопустимыеТипыЗначений.СодержитТип(ТипЗнч(Значение)) Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Невозможно сохранить значение ""%1"" типа ""%2""
						           |при обновлении ключей доступа списка ""%3"",
						           |так как этот тип не указан в определяемом типе %4.'"),
						Строка(Значение),
						Строка(ТипЗнч(Значение)),
						ПараметрыОбновления.Список,
						"ЗначениеДоступа");
					ВызватьИсключение ТекстОшибки;
				КонецЕсли;
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьКлючиДоступаПорцииЭлементовДанных.
Процедура ПодготовитьНовыйКлючДоступа(ОписаниеКлюча, ОписаниеНовыхКлючей, ПараметрыОбновления)
	
	НоваяСсылка = Справочники.КлючиДоступа.ПолучитьСсылку();
	НовыйКлюч = СлужебныйЭлемент(Справочники.КлючиДоступа); // СправочникОбъект.КлючиДоступа
	
	НовыйКлюч.УстановитьСсылкуНового(НоваяСсылка);
	НовыйКлюч.Наименование            = Строка(НоваяСсылка.УникальныйИдентификатор());
	НовыйКлюч.Список                  = ПараметрыОбновления.ИдентификаторСписка;
	НовыйКлюч.СоставПолей             = ПараметрыОбновления.СоставПолей;
	НовыйКлюч.ДляВнешнихПользователей = ПараметрыОбновления.ДляВнешнихПользователей;
	НовыйКлюч.Хеш                     = ОписаниеКлюча.Хеш;
	
	ВсеЗначенияТаблиц = ОписаниеНовыхКлючей.ЗначенияТаблиц;
	
	Для Каждого ЗначенияТаблицы Из ОписаниеКлюча.ЗначенияТаблиц Цикл
		ВсеЗначенияТаблицы = ВсеЗначенияТаблиц[ЗначенияТаблицы.ИмяТаблицы]; // См. ЗначенияТаблицыКлюча
		
		Если СтрНачинаетсяС(ЗначенияТаблицы.ИмяТаблицы, "Шапка") Тогда
			НоваяСтрока = ВсеЗначенияТаблицы.Добавить();
			ЗаполнитьЗначенияСвойств(НоваяСтрока, ЗначенияТаблицы.Таблица[0]);
			НоваяСтрока.Ссылка = НоваяСсылка;
			
			Если СтрЗаканчиваетсяНа(ЗначенияТаблицы.ИмяТаблицы, "0") Тогда
				ЗаполнитьЗначенияСвойств(НовыйКлюч, ЗначенияТаблицы.Таблица[0],, "Родитель");
			Иначе
				НомерСтроки = Число(Прав(ЗначенияТаблицы.ИмяТаблицы, 1));
				НовыйКлюч.Шапка.Добавить();
				ЗаполнитьЗначенияСвойств(НовыйКлюч.Шапка[НомерСтроки - 1], ЗначенияТаблицы.Таблица[0]);
				НоваяСтрока.НомерСтроки = НомерСтроки;
			КонецЕсли;
		Иначе
			Для Каждого Строка Из ЗначенияТаблицы.Таблица Цикл
				ТабличнаяЧастьКлюча = НовыйКлюч[ЗначенияТаблицы.ИмяТаблицы]; // ТабличнаяЧасть
				ЗаполнитьЗначенияСвойств(ТабличнаяЧастьКлюча.Добавить(), Строка);
				НоваяСтрока = ВсеЗначенияТаблицы.Добавить();
				ЗаполнитьЗначенияСвойств(НоваяСтрока, Строка);
				НоваяСтрока.Ссылка = НоваяСсылка;
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
	ОписаниеКлюча.Вставить("КлючДоступаОбъект", НовыйКлюч);
	ОписаниеНовыхКлючей.КлючиДоступа.Добавить().Ссылка = НоваяСсылка;
	ОписаниеНовыхКлючей.ОписанияКлючейПоСсылке.Вставить(НоваяСсылка, ОписаниеКлюча);
	
КонецПроцедуры

// Для процедур ОбновитьПорциюЭлементов, ОбновитьПраваНаКлючиДоступа.
Процедура ОбновитьПраваПорцииКлючейДоступаСписка(ОписаниеКлючейДоступа, ПараметрыОбновления, ЭтоНовыеКлючи = Ложь)
	
	Если Не ПараметрыОбновления.Свойство("Кэш") Тогда
		ПараметрыОбновления.Вставить("Кэш", Новый Структура);
	КонецЕсли;
	КлючиДоступа = ?(ЭтоНовыеКлючи, ОписаниеКлючейДоступа.КлючиДоступа, ОписаниеКлючейДоступа);
	
	Если ЗначениеЗаполнено(ПараметрыОбновления.СоставПолей) Тогда
		Запрос = Новый Запрос;
		Запрос.Текст = ПараметрыОбновления.ТекстЗапросаЗначенийИзКлючейДоступаДляРасчетаПрав;
		НомерТаблицы = 0;
		
		Если ЭтоНовыеКлючи Тогда
			ТекстыЗапроса = Новый Массив;
			РеквизитыТаблицКлюча = ПараметрыОбновления.РеквизитыТаблицКлюча;
			Шаблон =
			"ВЫБРАТЬ
			|	&Поля
			|ПОМЕСТИТЬ Таблица
			|ИЗ
			|	&Таблица КАК Таблица";
			Для Каждого ТаблицаКлюча Из ПараметрыОбновления.ТаблицыКлюча Цикл
				ПолеНомерСтроки = "";
				Если СтрНачинаетсяС(ТаблицаКлюча, "Шапка") Тогда
					Если СтрЗаканчиваетсяНа(ТаблицаКлюча, "0") Тогда
						ИмяВременнойТаблицы = "СправочникКлючиДоступа";
						Запрос.Текст = СтрЗаменить(Запрос.Текст,
							"@Справочник.КлючиДоступа ", ИмяВременнойТаблицы + " ");
					Иначе
						ИмяВременнойТаблицы = "СправочникКлючиДоступа" + ТаблицаКлюча;
						ПолеНомерСтроки = "НомерСтроки, "; // @query-part-1
						Запрос.Текст = СтрЗаменить(Запрос.Текст,
							"@Справочник.КлючиДоступа.Шапка КАК " + ТаблицаКлюча, // @query-part-1
							ИмяВременнойТаблицы + " КАК " + ТаблицаКлюча); // @query-part-1
					КонецЕсли;
				Иначе
					ИмяВременнойТаблицы = "СправочникКлючиДоступа" + ТаблицаКлюча;
					Запрос.Текст = СтрЗаменить(Запрос.Текст,
						"@Справочник.КлючиДоступа." + ТаблицаКлюча, ИмяВременнойТаблицы);
				КонецЕсли;
				ТекстЗапроса = СтрЗаменить(Шаблон, "Таблица", ИмяВременнойТаблицы);
				РеквизитыТаблицы = РеквизитыТаблицКлюча.Получить(ТаблицаКлюча);
				Поля = "Ссылка, " + ПолеНомерСтроки + СтрСоединить(РеквизитыТаблицы, ", ");
				ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Поля", Поля);
				ТекстыЗапроса.Добавить(ТекстЗапроса);
				Запрос.УстановитьПараметр(ИмяВременнойТаблицы, ОписаниеКлючейДоступа.ЗначенияТаблиц[ТаблицаКлюча]);
				НомерТаблицы = НомерТаблицы + 1;
			КонецЦикла;
			ТекстыЗапроса.Добавить(Запрос.Текст);
			Запрос.Текст = СтрСоединить(ТекстыЗапроса, ОбщегоНазначения.РазделительПакетаЗапросов());
		Иначе
			Запрос.Текст = СтрЗаменить(Запрос.Текст, "@Справочник.КлючиДоступа", "Справочник.КлючиДоступа");
		КонецЕсли;
		Запрос.УстановитьПараметр("КлючиДоступа", КлючиДоступа);
		
		Запрос.УстановитьПараметр("ИдентификаторТаблицыНастроекПрав",
			ПараметрыОбновления.ИдентификаторТаблицыНастроекПрав);
		
		Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
			ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
		
		УстановитьУточнениеПланаЗапроса(Запрос.Текст);
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
	Иначе
		РезультатыЗапроса = Новый Массив;
	КонецЕсли;
	
	ПараметрыОбновления.Вставить("ТипПользователя", ?(ПараметрыОбновления.ДляВнешнихПользователей,
		Тип("СправочникСсылка.ВнешниеПользователи"), Тип("СправочникСсылка.Пользователи")));
	
	ПараметрыОбновления.Вставить("ТипГруппыПользователей", ?(ПараметрыОбновления.ДляВнешнихПользователей,
		Тип("СправочникСсылка.ГруппыВнешнихПользователей"), Тип("СправочникСсылка.ГруппыПользователей")));
	
	ПараметрыОбновления.Вставить("ТипГруппыДоступа",    Тип("СправочникСсылка.ГруппыДоступа"));
	ПараметрыОбновления.Вставить("ПустаяГруппаДоступа", Справочники.ГруппыДоступа.ПустаяСсылка());
	
	ЗначенияТаблицКлючей = НовыеЗначенияТаблицКлючей();
	Для Каждого ТаблицаКлюча Из ПараметрыОбновления.ТаблицыКлюча Цикл
		НомерТаблицы = НомерТаблицы + 1;
		ЗначенияТаблицКлючей.Вставить(ТаблицаКлюча,
			РезультатыЗапроса[НомерТаблицы].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам));
	КонецЦикла;
	ЗаполнитьПраваНаВедущиеКлючиДоступаИВедущиеСписки(РезультатыЗапроса, НомерТаблицы, ПараметрыОбновления);
	ЗаполнитьПраваПоВладельцамНастроекПрав(РезультатыЗапроса, НомерТаблицы, ПараметрыОбновления);
	
	КэшРасчетаПрав = КэшРасчетаПравДляВидаПользователей(ПараметрыОбновления.ДляВнешнихПользователей);
	Если Не ПараметрыОбновления.Кэш.Свойство("ПраваРолейФункцииПравоДоступа")
	 Или КэшРасчетаПрав.РолиПрофилейГруппДоступа = Неопределено Тогда
		ПараметрыОбновления.Кэш.Вставить("ПраваРолейФункцииПравоДоступа",        Новый Соответствие);
		ПараметрыОбновления.Кэш.Вставить("ОбъектыМетаданныхФункцииПравоДоступа", Новый Соответствие);
		ПараметрыОбновления.Кэш.Вставить("ПраваПрофилейФункцииПравоДоступа",     Новый Соответствие);
	КонецЕсли;
	
	ЗаполнитьПраваГруппДоступаСписка(ПараметрыОбновления, КэшРасчетаПрав);
	ЗаполнитьЗначенияГруппДоступаДляРасчетаПрав(ПараметрыОбновления, КэшРасчетаПрав);
	ЗаполнитьПользователейГруппПользователей(ПараметрыОбновления, КэшРасчетаПрав);
	ЗаполнитьУчастниковГруппДоступаСписка(ПараметрыОбновления, КэшРасчетаПрав);
	ЗаполнитьГруппыПользователейКакЗначенияДоступа(ПараметрыОбновления, КэшРасчетаПрав);
	ЗаполнитьРолиИГруппыДоступаПрофилей(ПараметрыОбновления, КэшРасчетаПрав);
	
	ЗначенияПервойТаблицы = ?(ЗначениеЗаполнено(ПараметрыОбновления.СоставПолей),
		ЗначенияТаблицКлючей.Получить(ПараметрыОбновления.ТаблицыКлюча[0]),
		Новый Структура("Строки", КлючиДоступа));
	ИндексПоследнегоКлюча = ЗначенияПервойТаблицы.Строки.Количество() - 1;
	
	Для ИндексКлюча = 0 По ИндексПоследнегоКлюча Цикл
		ЗначенияТаблицКлюча = НовыеЗначенияТаблицКлюча();
		Для Каждого ТаблицаКлюча Из ПараметрыОбновления.ТаблицыКлюча Цикл
			ЗначенияТаблицы = ЗначенияТаблицКлючей.Получить(ТаблицаКлюча).Строки[ИндексКлюча].Строки;
			Если СтрНачинаетсяС(ТаблицаКлюча, "Шапка") Тогда
				ЗначенияТаблицы = ЗначенияТаблицы[0];
			КонецЕсли;
			ЗначенияТаблицКлюча.Вставить(ТаблицаКлюча, ЗначенияТаблицы);
		КонецЦикла;
		СтрокаЗначений = ЗначенияПервойТаблицы.Строки[ИндексКлюча]; // СправочникОбъект.КлючиДоступа
		КлючДоступа = СтрокаЗначений.Ссылка;
		
		ПраваНаКлюч = ПраваНаКлючДоступаСписка(ЗначенияТаблицКлюча, ПараметрыОбновления);
		// @skip-check query-in-loop - Порционная обработка данных
		ОбновитьПраваНаКлючДоступаСписка(КлючДоступа, ПраваНаКлюч,
			?(ЭтоНовыеКлючи, ОписаниеКлючейДоступа, Неопределено), ПараметрыОбновления);
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура из КлючИЗначение:
//   * Ключ - Строка - имя таблицы ключа
//   * Значение - ДеревоЗначений
//
Функция НовыеЗначенияТаблицКлючей()
	Возврат Новый Соответствие;
КонецФункции

// Возвращаемое значение:
//  Структура из КлючИЗначение:
//   * Ключ - Строка - имя таблицы ключа
//   * Значение - см. НовыеЗначенияТаблицыКлюча
//
Функция НовыеЗначенияТаблицКлюча()
	Возврат Новый Структура;
КонецФункции

// Возвращаемое значение:
//   - КоллекцияСтрокДереваЗначений
//   - ТаблицаЗначений
//   - СтрокаДереваЗначений
//
Функция НовыеЗначенияТаблицыКлюча()
	Возврат Новый ТаблицаЗначений;
КонецФункции

// Для процедуры ОбновитьПорциюЭлементов.
Процедура ОбработатьУстаревшиеКлючиДоступаСписка(ЭлементыДанных, ПараметрыОбновления)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК Поле1
	|ИЗ
	|	РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
	|ГДЕ
	|	КлючиДоступаГруппДоступа.КлючДоступа = &КлючДоступа
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК Поле1
	|ИЗ
	|	РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК КлючиДоступаНаборовГруппДоступа
	|ГДЕ
	|	КлючиДоступаНаборовГруппДоступа.КлючДоступа = &КлючДоступа
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК Поле1
	|ИЗ
	|	РегистрСведений.КлючиДоступаПользователей КАК КлючиДоступаПользователей
	|ГДЕ
	|	КлючиДоступаПользователей.КлючДоступа = &КлючДоступа
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК Поле1
	|ИЗ
	|	РегистрСведений.КлючиДоступаВнешнихПользователей КАК КлючиДоступаВнешнихПользователей
	|ГДЕ
	|	КлючиДоступаВнешнихПользователей.КлючДоступа = &КлючДоступа";
	
	ЭтоОчисткаВыбранныхКлючей = ПараметрыОбновления.БезЗаписиКлючейДоступа
		Или ПараметрыОбновления.СЗаписьюКлючаДоступаДляЗависимыхСписковБезКлючей;
	
	Если Не ЭтоОчисткаВыбранныхКлючей Тогда
		ЗапросИспользованияКлюча = Новый Запрос;
		ЗапросИспользованияКлюча.Текст =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА КАК ЗначениеИстина
		|ИЗ
		|	Справочник.КлючиДоступа КАК КлючиДоступа
		|ГДЕ
		|	КлючиДоступа.Ссылка = &Ссылка
		|	И КлючиДоступа.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)";
	КонецЕсли;
	
	НаборЗаписейГруппДоступаКлюча         = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаГруппДоступа);
	НаборЗаписейНаборовГруппДоступаКлюча  = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаНаборовГруппДоступа);
	НаборЗаписейПользователейКлюча        = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаПользователей);
	НаборЗаписейВнешнихПользователейКлюча = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаВнешнихПользователей);
	
	Для Каждого Строка Из ЭлементыДанных Цикл
		СтрокаЗначений = Строка; // СправочникОбъект.КлючиДоступа
		Ссылка = СтрокаЗначений.Ссылка;
		Если Не ЭтоОчисткаВыбранныхКлючей И Строка.Используется Тогда
			ЗапросИспользованияКлюча.УстановитьПараметр("Ссылка", Ссылка);
			// @skip-check query-in-loop - Порционная обработка данных
			Если Не ЗапросИспользованияКлюча.Выполнить().Пустой() Тогда
				Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 1) Тогда
					Прервать;
				КонецЕсли;
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		
		Блокировка = Новый БлокировкаДанных;
		Если Не ЭтоОчисткаВыбранныхКлючей Тогда
			ЭлементБлокировки = Блокировка.Добавить("Справочник.КлючиДоступа");
			ЭлементБлокировки.УстановитьЗначение("Список",      Строка.Список);
			ЭлементБлокировки.УстановитьЗначение("Хеш",         Строка.Хеш);
			ЭлементБлокировки.УстановитьЗначение("СоставПолей", Строка.СоставПолей);
			ЭлементБлокировки.УстановитьЗначение("ДляВнешнихПользователей",
				ПараметрыОбновления.ДляВнешнихПользователей);
		КонецЕсли;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.КлючиДоступа");
		ЭлементБлокировки.УстановитьЗначение("Ссылка", Ссылка);
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаГруппДоступа");
		ЭлементБлокировки.УстановитьЗначение("КлючДоступа", Ссылка);
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаНаборовГруппДоступа");
		ЭлементБлокировки.УстановитьЗначение("КлючДоступа", Ссылка);
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаПользователей");
		ЭлементБлокировки.УстановитьЗначение("КлючДоступа", Ссылка);
		ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаВнешнихПользователей");
		ЭлементБлокировки.УстановитьЗначение("КлючДоступа", Ссылка);
		
		НачатьТранзакцию();
		Попытка
			Блокировка.Заблокировать();
			Объект = СлужебныйЭлемент(Неопределено, Ссылка);
			УдалитьКлюч = Ложь;
			Если ЭтоОчисткаВыбранныхКлючей Или Объект = Неопределено Тогда
				УдалитьКлюч = Истина;
			ИначеЕсли Строка.Используется Тогда
				Объект.НеИспользуетсяС = '00010101';
			ИначеЕсли Не Строка.Удалить Тогда
				Объект.НеИспользуетсяС = ТекущаяДатаСеанса();
			ИначеЕсли ЗначениеЗаполнено(Объект.НеИспользуетсяС) Тогда
				УдалитьКлюч = Истина;
			КонецЕсли;
			Если УдалитьКлюч Тогда
				Запрос.УстановитьПараметр("КлючДоступа", Ссылка);
				// @skip-check query-in-loop - Порционная обработка данных
				РезультатыЗапроса = Запрос.ВыполнитьПакет();
				Если Не РезультатыЗапроса[0].Пустой() Тогда
					НаборЗаписейГруппДоступаКлюча.Отбор.КлючДоступа.Установить(Ссылка);
					НаборЗаписейГруппДоступаКлюча.Записать();
				КонецЕсли;
				Если Не РезультатыЗапроса[1].Пустой() Тогда
					НаборЗаписейНаборовГруппДоступаКлюча.Отбор.КлючДоступа.Установить(Ссылка);
					НаборЗаписейНаборовГруппДоступаКлюча.Записать();
				КонецЕсли;
				Если Не РезультатыЗапроса[2].Пустой() Тогда
					НаборЗаписейПользователейКлюча.Отбор.КлючДоступа.Установить(Ссылка);
					НаборЗаписейПользователейКлюча.Записать();
				КонецЕсли;
				Если Не РезультатыЗапроса[3].Пустой() Тогда
					НаборЗаписейВнешнихПользователейКлюча.Отбор.КлючДоступа.Установить(Ссылка);
					НаборЗаписейВнешнихПользователейКлюча.Записать();
				КонецЕсли;
			КонецЕсли;
			Если Объект <> Неопределено Тогда
				Если УдалитьКлюч Тогда
					Объект.Удалить();
				ИначеЕсли Объект.Модифицированность() Тогда
					Объект.Записать();
				КонецЕсли;
			КонецЕсли;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
		
		Если ТребуетсяПрерватьОбработкуЭлементов(ПараметрыОбновления, 1) Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
//
// Возвращаемое значение:
//  Структура:
//   * ПраваГруппДоступаСписков - Соответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//              - СправочникСсылка.ИдентификаторыОбъектовРасширений - идентификатор списка
//      ** Значение - см. ПраваГруппДоступаСписка
//   
//   * ЗначенияГруппДоступа                  - см. НовыеЗначенияГруппДоступа
//   * УчастникиГруппДоступа                 - см. НовыеУчастникиГруппДоступа
//   * ПользователиГруппПользователей        - см. НовыеПользователиГруппПользователей
//   * ГруппыПользователейГруппДоступа       - см. НовыеГруппыПользователейГруппДоступа
//   * ГруппыПользователейКакЗначенияДоступа - см. НовыеГруппыПользователейКакЗначенияДоступа
//   * РолиПрофилейГруппДоступа              - см. НовыеРолиПрофилейГруппДоступа
//   * ГруппыДоступаПрофилей                 - см. НовыеГруппыДоступаПрофилей
//
Функция КэшРасчетаПравДляВидаПользователей(ДляВнешнихПользователей) Экспорт
	
	КлючДанныхПовторногоИспользования = Строка(ПараметрыСеанса.КлючДанныхПовторногоИспользования);
	Кэш = УправлениеДоступомСлужебныйПовтИсп.КэшРасчетаПрав(КлючДанныхПовторногоИспользования);
	
	ВерсияДанных = ВерсияДанныхДляКэшаРасчетаПрав();
	
	Если Кэш.ВерсияДанных.ТаблицыГруппДоступа <> ВерсияДанных.ТаблицыГруппДоступа Тогда
		СброситьКэшРасчетаПрав(Кэш, "ПраваГруппДоступаСписков", Истина);
		Кэш.ВерсияДанных.ТаблицыГруппДоступа = ВерсияДанных.ТаблицыГруппДоступа;
	КонецЕсли;
	
	Если Кэш.ВерсияДанных.ЗначенияГруппДоступа <> ВерсияДанных.ЗначенияГруппДоступа Тогда
		СброситьКэшРасчетаПрав(Кэш, "ЗначенияГруппДоступа");
		СброситьКэшРасчетаПрав(Кэш, "ГруппыПользователейКакЗначенияДоступа");
		Кэш.ВерсияДанных.ЗначенияГруппДоступа = ВерсияДанных.ЗначенияГруппДоступа;
	КонецЕсли;
	
	Если Кэш.ВерсияДанных.УчастникиГруппДоступа <> ВерсияДанных.УчастникиГруппДоступа Тогда
		СброситьКэшРасчетаПрав(Кэш, "УчастникиГруппДоступа");
		СброситьКэшРасчетаПрав(Кэш, "ГруппыПользователейГруппДоступа");
		Кэш.ВерсияДанных.УчастникиГруппДоступа = ВерсияДанных.УчастникиГруппДоступа;
	КонецЕсли;
	
	Если Кэш.ВерсияДанных.СоставыГруппПользователей <> ВерсияДанных.СоставыГруппПользователей Тогда
		СброситьКэшРасчетаПрав(Кэш, "ПользователиГруппПользователей");
		СброситьКэшРасчетаПрав(Кэш, "УчастникиГруппДоступа");
		СброситьКэшРасчетаПрав(Кэш, "ГруппыПользователейГруппДоступа");
		СброситьКэшРасчетаПрав(Кэш, "ГруппыПользователейКакЗначенияДоступа");
		Кэш.ВерсияДанных.СоставыГруппПользователей = ВерсияДанных.СоставыГруппПользователей;
	КонецЕсли;
	
	Если Кэш.ВерсияДанных.РолиПрофилейГруппДоступа <> ВерсияДанных.РолиПрофилейГруппДоступа Тогда
		СброситьКэшРасчетаПрав(Кэш, "РолиПрофилейГруппДоступа");
		СброситьКэшРасчетаПрав(Кэш, "ГруппыДоступаПрофилей");
		Кэш.ВерсияДанных.РолиПрофилейГруппДоступа = ВерсияДанных.РолиПрофилейГруппДоступа;
	КонецЕсли;
	
	Если Кэш.ВерсияДанных.ГруппыДоступаПрофилей <> ВерсияДанных.ГруппыДоступаПрофилей Тогда
		СброситьКэшРасчетаПрав(Кэш, "РолиПрофилейГруппДоступа");
		СброситьКэшРасчетаПрав(Кэш, "ГруппыДоступаПрофилей");
		Кэш.ВерсияДанных.ГруппыДоступаПрофилей = ВерсияДанных.ГруппыДоступаПрофилей;
	КонецЕсли;
	
	Если ДляВнешнихПользователей Тогда
		Возврат Кэш.ДляВнешнихПользователей;
	Иначе
		Возврат Кэш.ДляПользователей;
	КонецЕсли;
	
КонецФункции

// Для функции КэшРасчетаПравДляВидаПользователей.
Процедура СброситьКэшРасчетаПрав(Кэш, Свойство, НовоеСоответствие = Ложь)
	
	Кэш.ДляПользователей[Свойство]        = ?(НовоеСоответствие, Новый Соответствие, Неопределено);
	Кэш.ДляВнешнихПользователей[Свойство] = ?(НовоеСоответствие, Новый Соответствие, Неопределено);
	
КонецПроцедуры

// Для процедур ЗаполнитьПраваГруппДоступаСписка и
// ЗаполнитьУчастниковГруппДоступаСписка.
//
Функция ТекстЗапросаНазначенияПрофилей()
	
	Возврат
	"ВЫБРАТЬ
	|	Профили.Ссылка КАК Профиль,
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА НазначениеПрофилей.ТипПользователей ЕСТЬ NULL
	|				ТОГДА ИСТИНА
	|			ИНАЧЕ ТИПЗНАЧЕНИЯ(НазначениеПрофилей.ТипПользователей) = ТИП(Справочник.Пользователи)
	|		КОНЕЦ) КАК ДляПользователей,
	|	МАКСИМУМ(ВЫБОР
	|			КОГДА НазначениеПрофилей.ТипПользователей ЕСТЬ NULL
	|				ТОГДА ЛОЖЬ
	|			ИНАЧЕ НазначениеПрофилей.ТипПользователей <> НЕОПРЕДЕЛЕНО
	|					И ТИПЗНАЧЕНИЯ(НазначениеПрофилей.ТипПользователей) <> ТИП(Справочник.Пользователи)
	|		КОНЕЦ) КАК ДляВнешнихПользователей
	|ПОМЕСТИТЬ НазначениеПрофилей
	|ИЗ
	|	Справочник.ПрофилиГруппДоступа КАК Профили
	|		ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Назначение КАК НазначениеПрофилей
	|		ПО (НазначениеПрофилей.Ссылка = Профили.Ссылка)
	|
	|СГРУППИРОВАТЬ ПО
	|	Профили.Ссылка";
	
КонецФункции

// Параметры:
//  Права - ТаблицаЗначений
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//   * ГруппаДоступа - СправочникСсылка.ГруппыДоступа
//   * ПравоИзменение - Булево
//   * ПравоДобавление - Булево
//   * ПравоЧтениеБезОграничения - Булево
//   * ПравоИзменениеБезОграничения - Булево
//   * ПравоДобавлениеБезОграничения - Булево
//
Функция НовыеПраваГруппДоступаСписка(Права)
	Возврат Права;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьПраваГруппДоступаСписка(ПараметрыОбновления, Кэш)
	
	ИдентификаторСписка = ПараметрыОбновления.ИдентификаторСписка;
	ПраваГруппДоступаСписка = Кэш.ПраваГруппДоступаСписков.Получить(ИдентификаторСписка);
	Если ПраваГруппДоступаСписка <> Неопределено Тогда
		ПараметрыОбновления.Вставить("ПраваГруппДоступаСписка", ПраваГруппДоступаСписка);
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("Таблица", ИдентификаторСписка);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ТаблицыГруппДоступа.ГруппаДоступа КАК ГруппаДоступа,
	|	ТаблицыГруппДоступа.ПравоИзменение КАК ПравоИзменение,
	|	ТаблицыГруппДоступа.ПравоДобавление КАК ПравоДобавление,
	|	ТаблицыГруппДоступа.ПравоЧтениеБезОграничения КАК ПравоЧтениеБезОграничения,
	|	ТаблицыГруппДоступа.ПравоИзменениеБезОграничения КАК ПравоИзменениеБезОграничения,
	|	ТаблицыГруппДоступа.ПравоДобавлениеБезОграничения КАК ПравоДобавлениеБезОграничения
	|ИЗ
	|	РегистрСведений.ТаблицыГруппДоступа КАК ТаблицыГруппДоступа
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа КАК ГруппыДоступа
	|		ПО (ТаблицыГруппДоступа.Таблица = &Таблица)
	|			И (ГруппыДоступа.Ссылка = ТаблицыГруппДоступа.ГруппаДоступа)
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ НазначениеПрофилей КАК НазначениеПрофилей
	|		ПО (НазначениеПрофилей.Профиль = ГруппыДоступа.Профиль)
	|			И (НазначениеПрофилей.ДляПользователей)";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"НазначениеПрофилей.ДляПользователей", "НазначениеПрофилей.ДляВнешнихПользователей");
	КонецЕсли;
	
	Запрос.Текст = ТекстЗапросаНазначенияПрофилей()
		+ ОбщегоНазначения.РазделительПакетаЗапросов() + Запрос.Текст;
	
	ПраваГруппДоступаСписка = НовыеПраваГруппДоступаСписка(Запрос.Выполнить().Выгрузить());
	Кэш.ПраваГруппДоступаСписков.Вставить(ИдентификаторСписка, ПраваГруппДоступаСписка);
	ПараметрыОбновления.Вставить("ПраваГруппДоступаСписка", ПраваГруппДоступаСписка);
	
КонецПроцедуры

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.ГруппыПользователей
//          - СправочникСсылка.ГруппыВнешнихПользователей
//          - СправочникСсылка.Пользователи
//         - СправочникСсылка.ВнешниеПользователи
//   * Значение - Соответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.Пользователи
//             - СправочникСсылка.ВнешниеПользователи
//      ** Значение - Булево - Истина
//
Функция НовыеПользователиГруппПользователей()
	Возврат Новый Соответствие;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьПользователейГруппПользователей(ПараметрыОбновления, Кэш)
	
	ПараметрыОбновления.Вставить("ПользователиГруппПользователей", НовыеПользователиГруппПользователей());
	
	Если Не ПараметрыОбновления.РассчитыватьПраваПользователей Тогда
		Возврат;
	КонецЕсли;
	
	Если Кэш.ПользователиГруппПользователей <> Неопределено Тогда
		ПараметрыОбновления.ПользователиГруппПользователей = Кэш.ПользователиГруппПользователей;
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	СоставыГруппПользователей.ГруппаПользователей КАК ГруппаПользователей,
	|	СоставыГруппПользователей.Пользователь КАК Пользователь
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи
	|		ПО (Пользователи.Ссылка = СоставыГруппПользователей.Пользователь)
	|			И (Пользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)
	|			И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.ГруппаПользователей) = ТИП(Справочник.ГруппыПользователей))
	|			И (СоставыГруппПользователей.Используется)
	|ИТОГИ ПО
	|	ГруппаПользователей";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"Справочник.Пользователи", "Справочник.ВнешниеПользователи");
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
	КонецЕсли;
	
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	ПользователиГруппПользователей = ПараметрыОбновления.ПользователиГруппПользователей;
	
	РезультатЗапроса = Запрос.Выполнить();
	ЗаполнитьПользователейГрупп(ПользователиГруппПользователей, РезультатЗапроса);
	
	Кэш.ПользователиГруппПользователей = ПользователиГруппПользователей;
	
КонецПроцедуры

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.ГруппыПользователей
//          - СправочникСсылка.ГруппыВнешнихПользователей
//   * Значение - Соответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.Пользователи
//              - СправочникСсылка.ВнешниеПользователи
//      ** Значение - Булево - Истина
//
Функция НовыеГруппыПользователейКакЗначенияДоступа()
	Возврат Новый Соответствие;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьГруппыПользователейКакЗначенияДоступа(ПараметрыОбновления, Кэш)
	
	ПараметрыОбновления.Вставить("ГруппыПользователейКакЗначенияДоступа",
		НовыеГруппыПользователейКакЗначенияДоступа());
	
	Если Не ПараметрыОбновления.РассчитыватьПраваПользователей Тогда
		Возврат;
	КонецЕсли;
	
	Если Кэш.ГруппыПользователейКакЗначенияДоступа <> Неопределено Тогда
		ПараметрыОбновления.ГруппыПользователейКакЗначенияДоступа = Кэш.ГруппыПользователейКакЗначенияДоступа;
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	СоставыГруппПользователей.ГруппаПользователей КАК ГруппаПользователей,
	|	СоставыГруппПользователей.Пользователь КАК Пользователь
	|ИЗ
	|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ЗначенияГруппДоступа КАК ЗначенияГруппДоступа
	|		ПО СоставыГруппПользователей.ГруппаПользователей = ЗначенияГруппДоступа.ЗначениеДоступа
	|			И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.ГруппаПользователей) = ТИП(Справочник.ГруппыПользователей))
	|			И (ТИПЗНАЧЕНИЯ(ЗначенияГруппДоступа.ЗначениеДоступа) = ТИП(Справочник.ГруппыПользователей))
	|ИТОГИ ПО
	|	ГруппаПользователей";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
	КонецЕсли;
	
	ГруппыПользователейКакЗначенияДоступа = ПараметрыОбновления.ГруппыПользователейКакЗначенияДоступа;
	
	РезультатЗапроса = Запрос.Выполнить();
	ЗаполнитьПользователейГрупп(ГруппыПользователейКакЗначенияДоступа, РезультатЗапроса);
	
	Кэш.ГруппыПользователейКакЗначенияДоступа = ГруппыПользователейКакЗначенияДоступа;
	
КонецПроцедуры

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.ГруппыДоступа
//   * Значение - Соответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.Пользователи
//              - СправочникСсылка.ГруппыПользователей
//              - СправочникСсылка.ВнешниеПользователи
//              - СправочникСсылка.ГруппыВнешнихПользователей
//      ** Значение - Булево - Истина
//
Функция НовыеУчастникиГруппДоступа()
	Возврат Новый Соответствие;
КонецФункции

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.ГруппыДоступа
//   * Значение - Соответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.ГруппыПользователей
//              - СправочникСсылка.ГруппыВнешнихПользователей
//      ** Значение - Соответствие из КлючИЗначение:
//          *** Ключ - СправочникСсылка.Пользователи
//                   - СправочникСсылка.ВнешниеПользователи
//          *** Значение - Булево - Истина
//
Функция НовыеГруппыПользователейГруппДоступа()
	Возврат Новый Соответствие;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьУчастниковГруппДоступаСписка(ПараметрыОбновления, Кэш)
	
	ПараметрыОбновления.Вставить("УчастникиГруппДоступа",           НовыеУчастникиГруппДоступа());
	ПараметрыОбновления.Вставить("ГруппыПользователейГруппДоступа", НовыеГруппыПользователейГруппДоступа());
	
	Если Не ПараметрыОбновления.РассчитыватьПраваПользователей Тогда
		Возврат;
	КонецЕсли;
	
	Если Кэш.УчастникиГруппДоступа <> Неопределено Тогда
		ПараметрыОбновления.УчастникиГруппДоступа           = Кэш.УчастникиГруппДоступа;
		ПараметрыОбновления.ГруппыПользователейГруппДоступа = Кэш.ГруппыПользователейГруппДоступа;
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ПользователиГруппыДоступа.Ссылка КАК ГруппаДоступа,
	|	ПользователиГруппыДоступа.Пользователь КАК Участник
	|ИЗ
	|	Справочник.ГруппыДоступа КАК ГруппыДоступа
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ НазначениеПрофилей КАК НазначениеПрофилей
	|		ПО (НазначениеПрофилей.Профиль = ГруппыДоступа.Профиль)
	|			И (НазначениеПрофилей.ДляПользователей)
	|			И (НЕ ГруппыДоступа.ПометкаУдаления)
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа.Пользователи КАК ПользователиГруппыДоступа
	|		ПО (ПользователиГруппыДоступа.Ссылка = ГруппыДоступа.Ссылка)
	|			И (ИСТИНА В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ИСТИНА
	|				ИЗ
	|					РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|						ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Пользователи КАК Пользователи
	|						ПО
	|							СоставыГруппПользователей.ГруппаПользователей = ПользователиГруппыДоступа.Пользователь
	|								И СоставыГруппПользователей.Используется
	|								И Пользователи.Ссылка = СоставыГруппПользователей.Пользователь
	|								И Пользователи.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор))
	|ИТОГИ ПО
	|	ГруппаДоступа";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"НазначениеПрофилей.ДляПользователей", "НазначениеПрофилей.ДляВнешнихПользователей");
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"Справочник.Пользователи", "Справочник.ВнешниеПользователи");
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"Справочник.ГруппыПользователей", "Справочник.ГруппыВнешнихПользователей");
	КонецЕсли;
	
	Запрос.Текст = ТекстЗапросаНазначенияПрофилей()
		+ ОбщегоНазначения.РазделительПакетаЗапросов() + Запрос.Текст;
	
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	УчастникиГруппДоступа           = ПараметрыОбновления.УчастникиГруппДоступа;
	ГруппыПользователейГруппДоступа = ПараметрыОбновления.ГруппыПользователейГруппДоступа;
	ТипГруппыПользователей          = ПараметрыОбновления.ТипГруппыПользователей;
	ПользователиГруппПользователей  = ПараметрыОбновления.ПользователиГруппПользователей;
	
	Дерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	
	Для Каждого Строка Из Дерево.Строки Цикл
		УчастникиГруппыДоступа = Новый Соответствие;
		ГруппыПользователейГруппыДоступа = Новый Соответствие;
		Для Каждого Подстрока Из Строка.Строки Цикл
			УчастникиГруппыДоступа.Вставить(Подстрока.Участник, Истина);
			Если ТипЗнч(Подстрока.Участник) = ТипГруппыПользователей Тогда
				ПользователиГруппы = ПользователиГруппПользователей.Получить(Подстрока.Участник);
				Если ПользователиГруппы <> Неопределено Тогда
					ГруппыПользователейГруппыДоступа.Вставить(Подстрока.Участник, ПользователиГруппы);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Если УчастникиГруппыДоступа.Количество() > 0 Тогда
			УчастникиГруппДоступа.Вставить(Строка.ГруппаДоступа, УчастникиГруппыДоступа);
		КонецЕсли;
		Если ГруппыПользователейГруппыДоступа.Количество() > 0 Тогда
			ГруппыПользователейГруппДоступа.Вставить(Строка.ГруппаДоступа, ГруппыПользователейГруппыДоступа);
		КонецЕсли;
	КонецЦикла;
	
	Кэш.УчастникиГруппДоступа           = УчастникиГруппДоступа;
	Кэш.ГруппыПользователейГруппДоступа = ГруппыПользователейГруппДоступа;
	
КонецПроцедуры

// Для процедур ЗаполнитьПользователейГруппПользователей и
// ЗаполнитьГруппыПользователейКакЗначенияДоступа.
//
Процедура ЗаполнитьПользователейГрупп(ПользователиГруппПользователей, РезультатЗапроса)
	
	Дерево = РезультатЗапроса.Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	Для Каждого Строка Из Дерево.Строки Цикл
		ПользователиГруппыПользователей = Новый Соответствие;
		Для Каждого Подстрока Из Строка.Строки Цикл
			ПользователиГруппыПользователей.Вставить(Подстрока.Пользователь, Истина);
		КонецЦикла;
		ПользователиГруппПользователей.Вставить(Строка.ГруппаПользователей, ПользователиГруппыПользователей);
	КонецЦикла;
	
КонецПроцедуры

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - Тип - тип значений доступа
//   * Значение - Структура:
//      ** ВсеРазрешены - Булево
//      ** Значения - Соответствие из КлючИЗначение:
//          *** Ключ - ОпределяемыйТип.ЗначениеДоступа
//          *** Значение - Булево - Истина
//
Функция НовыеЗначенияГруппыДоступа()
	Возврат Новый Соответствие;
КонецФункции

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.ГруппыДоступа
//   * Значение - см. НовыеЗначенияГруппыДоступа
//
Функция НовыеЗначенияГруппДоступа()
	Возврат Новый Соответствие;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьЗначенияГруппДоступаДляРасчетаПрав(ПараметрыОбновления, Кэш)
	
	Если Кэш.ЗначенияГруппДоступа <> Неопределено Тогда
		ПараметрыОбновления.Вставить("ЗначенияГруппДоступа", Кэш.ЗначенияГруппДоступа);
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ЗначенияГруппДоступаПоУмолчанию.ГруппаДоступа КАК ГруппаДоступа,
	|	ТИПЗНАЧЕНИЯ(ЗначенияГруппДоступаПоУмолчанию.ТипЗначенийДоступа) КАК ТипЗначенийДоступа,
	|	ЗначенияГруппДоступаПоУмолчанию.ВсеРазрешены КАК ВсеРазрешены
	|ИЗ
	|	РегистрСведений.ЗначенияГруппДоступаПоУмолчанию КАК ЗначенияГруппДоступаПоУмолчанию
	|ГДЕ
	|	НЕ ЗначенияГруппДоступаПоУмолчанию.БезНастройки
	|ИТОГИ ПО
	|	ГруппаДоступа
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ЗначенияГруппДоступа.ГруппаДоступа КАК ГруппаДоступа,
	|	ТИПЗНАЧЕНИЯ(ЗначенияГруппДоступа.ЗначениеДоступа) КАК ТипЗначенийДоступа,
	|	ЗначенияГруппДоступа.ЗначениеДоступа КАК ЗначениеДоступа
	|ИЗ
	|	РегистрСведений.ЗначенияГруппДоступа КАК ЗначенияГруппДоступа
	|ИТОГИ ПО
	|	ГруппаДоступа,
	|	ТипЗначенийДоступа";
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	
	ЗначенияГруппДоступа = НовыеЗначенияГруппДоступа();
	
	Дерево = РезультатыЗапроса[0].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	Для Каждого Строка Из Дерево.Строки Цикл
		ЗначенияГруппыДоступа = НовыеЗначенияГруппыДоступа();
		Для Каждого Подстрока Из Строка.Строки Цикл
			ЗначенияОдногоТипа = Новый Структура;
			ЗначенияОдногоТипа.Вставить("ВсеРазрешены", Подстрока.ВсеРазрешены);
			ЗначенияОдногоТипа.Вставить("Значения",     Новый Соответствие);
			ЗначенияГруппыДоступа.Вставить(Подстрока.ТипЗначенийДоступа, ЗначенияОдногоТипа);
		КонецЦикла;
		ЗначенияГруппДоступа.Вставить(Строка.ГруппаДоступа, ЗначенияГруппыДоступа);
	КонецЦикла;
	
	Дерево = РезультатыЗапроса[1].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	Для Каждого Строка Из Дерево.Строки Цикл
		ЗначенияГруппыДоступа = ЗначенияГруппДоступа.Получить(Строка.ГруппаДоступа);
		Если ЗначенияГруппыДоступа = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Для Каждого Подстрока Из Строка.Строки Цикл
			ЗначенияОдногоТипа = ЗначенияГруппыДоступа.Получить(Подстрока.ТипЗначенийДоступа);
			Если ЗначенияОдногоТипа = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Значения = ЗначенияОдногоТипа.Значения;
			Для Каждого ОписаниеЗначения Из Подстрока.Строки Цикл
				Значения.Вставить(ОписаниеЗначения.ЗначениеДоступа, Истина);
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
	
	ПараметрыОбновления.Вставить("ЗначенияГруппДоступа", ЗначенияГруппДоступа);
	Кэш.ЗначенияГруппДоступа = ЗначенияГруппДоступа;
	
КонецПроцедуры

// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Профиль - СправочникСсылка.ПрофилиГруппДоступа
//   * Роль    - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//            - СправочникСсылка.ИдентификаторыОбъектовРасширений
//   * ИмяРоли - Строка
//   * ИмяРолиВерхнийРегистр - Строка
//
Функция НовыеРолиПрофилейГруппДоступа()
	Возврат Новый ТаблицаЗначений;
КонецФункции

// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Профиль       - СправочникСсылка.ПрофилиГруппДоступа
//   * ГруппаДоступа - СправочникСсылка.ГруппыДоступа
//
Функция НовыеГруппыДоступаПрофилей()
	Возврат Новый ТаблицаЗначений;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьРолиИГруппыДоступаПрофилей(ПараметрыОбновления, Кэш)
	
	ПараметрыОбновления.Вставить("РолиПрофилейГруппДоступа", НовыеРолиПрофилейГруппДоступа());
	ПараметрыОбновления.Вставить("ГруппыДоступаПрофилей",    НовыеГруппыДоступаПрофилей());
	
	Если Не ПараметрыОбновления.ЕстьФункцияПравоДоступаИлиРольДоступна Тогда
		Возврат;
	КонецЕсли;
	
	Если Кэш.РолиПрофилейГруппДоступа <> Неопределено Тогда
		ПараметрыОбновления.РолиПрофилейГруппДоступа = Кэш.РолиПрофилейГруппДоступа;
		ПараметрыОбновления.ГруппыДоступаПрофилей    = Кэш.ГруппыДоступаПрофилей;
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	РолиПрофилей.Роль КАК Роль
	|ИЗ
	|	НазначениеПрофилей КАК НазначениеПрофилей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК РолиПрофилей
	|		ПО (РолиПрофилей.Ссылка = НазначениеПрофилей.Профиль)
	|			И (НазначениеПрофилей.ДляПользователей)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	РолиПрофилей.Ссылка КАК Профиль,
	|	РолиПрофилей.Роль КАК Роль
	|ИЗ
	|	НазначениеПрофилей КАК НазначениеПрофилей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ПрофилиГруппДоступа.Роли КАК РолиПрофилей
	|		ПО (РолиПрофилей.Ссылка = НазначениеПрофилей.Профиль)
	|			И (НазначениеПрофилей.ДляПользователей)
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НазначениеПрофилей.Профиль КАК Профиль,
	|	ГруппыДоступа.Ссылка КАК ГруппаДоступа
	|ИЗ
	|	НазначениеПрофилей КАК НазначениеПрофилей
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.ГруппыДоступа КАК ГруппыДоступа
	|		ПО НазначениеПрофилей.Профиль = ГруппыДоступа.Профиль
	|			И (НазначениеПрофилей.ДляПользователей)";
	
	Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"НазначениеПрофилей.ДляПользователей", "НазначениеПрофилей.ДляВнешнихПользователей");
	КонецЕсли;
	
	Запрос.Текст = ТекстЗапросаНазначенияПрофилей()
		+ ОбщегоНазначения.РазделительПакетаЗапросов() + Запрос.Текст;
	
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	
	Роли = РезультатыЗапроса[1].Выгрузить().ВыгрузитьКолонку("Роль");
	ОбъектыМетаданныхРолей = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(Роли, Ложь);
	
	РолиПрофилейГруппДоступа = РезультатыЗапроса[2].Выгрузить();
	ТипИмениРоли = Метаданные.Справочники.ИдентификаторыОбъектовМетаданных.Реквизиты.Имя.Тип;
	РолиПрофилейГруппДоступа.Колонки.Добавить("ИмяРоли", ТипИмениРоли);
	РолиПрофилейГруппДоступа.Колонки.Добавить("ИмяРолиВерхнийРегистр", ТипИмениРоли);
	РолиПрофилейГруппДоступа.Индексы.Добавить("Роль");
	Отбор = Новый Структура("Роль");
	ТребуетсяПроверитьАктуальностьМетаданных = Ложь;
	
	Для Каждого ОписаниеРоли Из ОбъектыМетаданныхРолей Цикл
		Если ТипЗнч(ОписаниеРоли.Значение) <> Тип("ОбъектМетаданных") Тогда
			ТребуетсяПроверитьАктуальностьМетаданных = Истина;
			Продолжить;
		КонецЕсли;
		ИмяРоли = ОписаниеРоли.Значение.Имя;
		ИмяРолиВерхнийРегистр = ВРег(ИмяРоли);
		Отбор.Роль = ОписаниеРоли.Ключ;
		Строки = РолиПрофилейГруппДоступа.НайтиСтроки(Отбор);
		Для Каждого Строка Из Строки Цикл
			Строка.ИмяРоли = ИмяРоли;
			Строка.ИмяРолиВерхнийРегистр = ИмяРолиВерхнийРегистр;
		КонецЦикла;
	КонецЦикла;
	Если ТребуетсяПроверитьАктуальностьМетаданных Тогда
		ПроверитьАктуальностьМетаданных();
	КонецЕсли;
	РолиПрофилейГруппДоступа.Индексы.Добавить("Профиль,ИмяРолиВерхнийРегистр");
	
	ГруппыДоступаПрофилей = РезультатыЗапроса[3].Выгрузить();
	ГруппыДоступаПрофилей.Индексы.Добавить("ГруппаДоступа");
	
	ПараметрыОбновления.РолиПрофилейГруппДоступа = РолиПрофилейГруппДоступа;
	ПараметрыОбновления.ГруппыДоступаПрофилей    = ГруппыДоступаПрофилей;
	Кэш.РолиПрофилейГруппДоступа = РолиПрофилейГруппДоступа;
	Кэш.ГруппыДоступаПрофилей    = ГруппыДоступаПрофилей;
	
КонецПроцедуры

// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.КлючиДоступа
//   * Значение - ФиксированнаяСтруктура:
//      ** ОграничениеЧтенияОтключено - Булево
//      ** ОграничениеОтключено - Булево
//      ** ПоГруппамДоступа - ФиксированноеСоответствие из КлючИЗначение:
//          *** Ключ - СправочникСсылка.ГруппыДоступа
//          *** Значение - Булево - право изменение
//
Функция НовыеПраваНаСпискиВедущихКлючейДоступа()
	Возврат Новый Соответствие;
КонецФункции

// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.КлючиДоступа
//   * Значение - ФиксированноеСоответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.ГруппыДоступа
//              - СправочникСсылка.ГруппыПользователей
//              - СправочникСсылка.ГруппыВнешнихПользователей
//      ** Значение - Булево - право изменение
//
Функция НовыеПраваНаВедущиеКлючиДоступа()
	Возврат Новый Соответствие;
КонецФункции

// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//   * Ключ - СправочникСсылка.ИдентификаторыОбъектовМетаданных
//          - СправочникСсылка.ИдентификаторыОбъектовРасширений - идентификатор списка
//          - Тип - тип значений списка
//   * Значение - ФиксированноеСоответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.ГруппыДоступа
//              - СправочникСсылка.ГруппыПользователей
//              - СправочникСсылка.ГруппыВнешнихПользователей
//      ** Значение - Булево - право изменение
//
Функция НовыеПраваНаВедущиеСписки()
	Возврат Новый Соответствие;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьПраваНаВедущиеКлючиДоступаИВедущиеСписки(РезультатыЗапроса, НомерТаблицы, ПараметрыОбновления)
	
	ПраваНаСпискиВедущихКлючейДоступа = НовыеПраваНаСпискиВедущихКлючейДоступа();
	ПраваНаВедущиеКлючиДоступа = НовыеПраваНаВедущиеКлючиДоступа();
	Если ПараметрыОбновления.ЕстьВедущиеКлючиДоступа Тогда
		ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(ПараметрыОбновления.ИдентификаторТранзакции, Неопределено, Ложь);
		Если ПараметрыОбновления.ДляВнешнихПользователей Тогда
			ДополнительныйКонтекст = ДействующиеПараметры.ДополнительныйКонтекст.ДляВнешнихПользователей;
		Иначе
			ДополнительныйКонтекст = ДействующиеПараметры.ДополнительныйКонтекст.ДляПользователей;
		КонецЕсли;
		НомерТаблицы = НомерТаблицы + 1;
		ПраваНаСписки = Новый Соответствие;
		Дерево = РезультатыЗапроса[НомерТаблицы].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
		ИдентификаторыСписков = Дерево.Строки.ВыгрузитьКолонку("Список");
		Если ПараметрыОбновления.Кэш.Свойство("ОбъектыМетаданныхПоИдентификаторам") Тогда
			ОбъектыМетаданныхПоИдентификаторам = ПараметрыОбновления.Кэш.ОбъектыМетаданныхПоИдентификаторам;
			НенайденныеИдентификаторыСписков = Новый Массив;
			Для Каждого ИдентификаторСписка Из ИдентификаторыСписков Цикл
				Если ОбъектыМетаданныхПоИдентификаторам.Получить(ИдентификаторСписка) = Неопределено Тогда
					НенайденныеИдентификаторыСписков.Добавить(ИдентификаторСписка);
				КонецЕсли;
			КонецЦикла;
			Результат = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(
				НенайденныеИдентификаторыСписков, Ложь);
			Для Каждого КлючИЗначение Из Результат Цикл
				ОбъектыМетаданныхПоИдентификаторам.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
			КонецЦикла;
		Иначе
			ОбъектыМетаданныхПоИдентификаторам = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(
				ИдентификаторыСписков, Ложь);
		КонецЕсли;
		Для Каждого Строка Из Дерево.Строки Цикл
			ОбъектМетаданных = ОбъектыМетаданныхПоИдентификаторам.Получить(Строка.Список);
			Если ТипЗнч(ОбъектМетаданных) <> Тип("ОбъектМетаданных") Тогда
				Продолжить;
			КонецЕсли;
			ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
			ПоГруппамДоступа = Новый Соответствие;
			Для Каждого Подстрока Из Строка.Строки Цикл
				ПоГруппамДоступа.Вставить(Подстрока.ГруппаДоступа, Подстрока.ПравоИзменение);
			КонецЦикла;
			ПраваНаСписок = Новый Структура;
			ПраваНаСписок.Вставить("ОграничениеЧтенияОтключено",
				ДополнительныйКонтекст.СпискиСОтключеннымОграничениемЧтения.Получить(ПолноеИмя) <> Неопределено);
			ПраваНаСписок.Вставить("ОграничениеОтключено",
				ДополнительныйКонтекст.СпискиСОтключеннымОграничением.Получить(ПолноеИмя) <> Неопределено);
			ПраваНаСписок.Вставить("ПоГруппамДоступа", Новый ФиксированноеСоответствие(ПоГруппамДоступа));
			ПраваНаСписки.Вставить(Строка.Список, Новый ФиксированнаяСтруктура(ПраваНаСписок));
		КонецЦикла;
		НомерТаблицы = НомерТаблицы + 1;
		Таблица = РезультатыЗапроса[НомерТаблицы].Выгрузить();
		Для Каждого Строка Из Таблица Цикл
			ПраваНаСпискиВедущихКлючейДоступа.Вставить(Строка.КлючДоступа,
				ПраваНаСписки.Получить(Строка.Список));
		КонецЦикла;
		НомерТаблицы = НомерТаблицы + 1;
		Дерево = РезультатыЗапроса[НомерТаблицы].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
		Для Каждого Строка Из Дерево.Строки Цикл
			ПраваНаВедущийКлюч = Новый Соответствие;
			Для Каждого Подстрока Из Строка.Строки Цикл
				ПраваНаВедущийКлюч.Вставить(Подстрока.ВладелецПрав, Подстрока.ПравоИзменение);
			КонецЦикла;
			ПраваНаВедущиеКлючиДоступа.Вставить(Строка.КлючДоступа,
				Новый ФиксированноеСоответствие(ПраваНаВедущийКлюч));
		КонецЦикла;
	КонецЕсли;
	ПараметрыОбновления.Вставить("ПраваНаСпискиВедущихКлючейДоступа",
		Новый ФиксированноеСоответствие(ПраваНаСпискиВедущихКлючейДоступа));
	ПараметрыОбновления.Вставить("ПраваНаВедущиеКлючиДоступа",
		Новый ФиксированноеСоответствие(ПраваНаВедущиеКлючиДоступа));
	
	ПраваНаВедущиеСписки = НовыеПраваНаВедущиеСписки();
	Если ПараметрыОбновления.ЕстьВедущиеСпискиПоПравам Тогда
		НомерТаблицы = НомерТаблицы + 1;
		Дерево = РезультатыЗапроса[НомерТаблицы].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
		ДобавитьПраваПоТипам = Дерево.Колонки.Найти("ТипЗначения") <> Неопределено;
		Для Каждого Строка Из Дерево.Строки Цикл
			ПраваНаВедущийСписок = Новый Соответствие;
			Для Каждого Подстрока Из Строка.Строки Цикл
				ПраваНаВедущийСписок.Вставить(Подстрока.ВладелецПрав, Подстрока.ПравоИзменение);
				Если ДобавитьПраваПоТипам Тогда
					ТипЗначенияСписка = Подстрока.ТипЗначения;
				КонецЕсли;
			КонецЦикла;
			ПраваНаВедущиеСписки.Вставить(Строка.Список,
				Новый ФиксированноеСоответствие(ПраваНаВедущийСписок));
			Если ДобавитьПраваПоТипам Тогда
				ПраваНаВедущиеСписки.Вставить(ТипЗначенияСписка,
					Новый ФиксированноеСоответствие(ПраваНаВедущийСписок));
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	ПараметрыОбновления.Вставить("ПраваНаВедущиеСписки",
		Новый ФиксированноеСоответствие(ПраваНаВедущиеСписки));
	
КонецПроцедуры

// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//   * Ключ    - ОпределяемыйТип.ВладелецНастроекПрав
//   * Значение - ФиксированноеСоответствие из КлючИЗначение:
//      ** Ключ - СправочникСсылка.Пользователи
//              - СправочникСсылка.ГруппыПользователей
//              - СправочникСсылка.ВнешниеПользователи
//              - СправочникСсылка.ГруппыВнешнихПользователей
//      ** Значение - Булево - право изменение
//
Функция НовыеПраваПоВладельцамНастроекПрав()
	Возврат Новый Соответствие;
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ЗаполнитьПраваПоВладельцамНастроекПрав(РезультатыЗапроса, НомерТаблицы, ПараметрыОбновления)
	
	ПраваПоВладельцамНастроекПрав = НовыеПраваПоВладельцамНастроекПрав();
	Если ПараметрыОбновления.ЕстьВладельцыНастроекПрав Тогда
		НомерТаблицы = НомерТаблицы + 5;
		Дерево = РезультатыЗапроса[НомерТаблицы].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
		Для Каждого Строка Из Дерево.Строки Цикл
			ПраваПоВладельцуНастроекПрав = Новый Соответствие;
			Для Каждого Подстрока Из Строка.Строки Цикл
				ПраваПоВладельцуНастроекПрав.Вставить(Подстрока.ВладелецПрав, Подстрока.ПравоИзменение);
			КонецЦикла;
			ПраваПоВладельцамНастроекПрав.Вставить(Строка.ВладелецНастроекПрав,
				Новый ФиксированноеСоответствие(ПраваПоВладельцуНастроекПрав));
		КонецЦикла;
	КонецЕсли;
	ПараметрыОбновления.Вставить("ПраваПоВладельцамНастроекПрав",
		Новый ФиксированноеСоответствие(ПраваПоВладельцамНастроекПрав));
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура:
//   * ЗначенияТаблицКлюча - см. НовыеЗначенияТаблицКлюча
//   * БезЗаписиПраваЧтение - Булево
//   * РеквизитыТаблицКлюча                  - см. НовыеРеквизитыТаблицКлюча
//   * УчастникиГруппДоступа                 - см. НовыеУчастникиГруппДоступа
//   * ГруппыПользователейГруппДоступа       - см. НовыеГруппыПользователейГруппДоступа
//   * ГруппыПользователейКакЗначенияДоступа - см. НовыеГруппыПользователейКакЗначенияДоступа
//   * ПользователиГруппПользователей        - см. НовыеПользователиГруппПользователей
//   * ПраваНаСпискиВедущихКлючейДоступа     - см. НовыеПраваНаСпискиВедущихКлючейДоступа
//   * ПраваНаВедущиеКлючиДоступа            - см. НовыеПраваНаВедущиеКлючиДоступа
//   * ПраваНаВедущиеСписки                  - см. НовыеПраваНаВедущиеСписки
//   * ПраваПоВладельцамНастроекПрав         - см. НовыеПраваПоВладельцамНастроекПрав
//   * ТипГруппыДоступа - Тип
//   * ПустаяГруппаДоступа - СправочникСсылка.ГруппыДоступа
//   * ТипПользователя - Тип
//   * ТипГруппыПользователей - Тип
//   * ТипыВладельцевНастроекПрав - ФиксированноеСоответствие
//   * ПраваРолейФункцииПравоДоступа - Соответствие
//   * ОбъектыМетаданныхФункцииПравоДоступа - Соответствие
//   * ПраваПрофилейФункцииПравоДоступа - Соответствие
//   * РолиПрофилейГруппДоступа - см. РолиПрофилейГруппДоступа
//   * ГруппыДоступаПрофилей - см. ГруппыДоступаПрофилей
//   * ГруппаДоступа - СправочникСсылка.ГруппыДоступа - текущее значение
//   * ЗначенияГруппыДоступа - см. НовыеЗначенияГруппыДоступа
//   * ОписанияТребуемыхТаблицКлюча - Массив из Структура:
//      ** РеквизитыТаблиц    - Соответствие из КлючИЗначение:
//          *** Ключ     - Строка - имя таблицы ключа
//          *** Значение - Массив из Строка - имя реквизита таблицы ключа
//      ** ЗначенияТаблиц     - см. ТекущиеЗначенияТаблицКлюча
//      ** ИндексыСтрокТаблиц - Соответствие
//   * ТекущиеСтрокиТаблицКлюча - см. НовыеЗначенияТаблицКлюча
//
Функция НовыйКонтекстРасчетаПрав()
	Возврат Новый Структура;
КонецФункции

// Для процедуры ОбновитьПраваНаКлючиДоступаСписка.
Функция ПраваНаКлючДоступаСписка(ЗначенияТаблицКлюча, ПараметрыОбновления)
	
	ПраваНаКлюч = Новый Структура("ДляГрупп, ДляПользователей", Новый Соответствие, Новый Соответствие);
	
	Если ПараметрыОбновления.ОграничениеОтключено Тогда
		Если Не ПараметрыОбновления.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа Тогда
			Возврат ПраваНаКлюч;
		ИначеЕсли ПараметрыОбновления.ИзменениеРазрешеноДляВсехПользователей Тогда
			ПраваНаКлюч.ДляГрупп.Вставить(ПараметрыОбновления.ПустаяГруппаДоступа,
				Новый Структура("ПравоИзменение, ПравоДобавление", Истина, Истина));
			Возврат ПраваНаКлюч;
		КонецЕсли;
	КонецЕсли;
	
	БезЗаписиПраваЧтение = ПараметрыОбновления.ОграничениеЧтенияОтключено
		И Не ПараметрыОбновления.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа;
	
	Контекст = НовыйКонтекстРасчетаПрав();
	Контекст.Вставить("ЗначенияТаблицКлюча",                   ЗначенияТаблицКлюча);
	Контекст.Вставить("БезЗаписиПраваЧтение",                  БезЗаписиПраваЧтение);
	Контекст.Вставить("РеквизитыТаблицКлюча",                  ПараметрыОбновления.РеквизитыТаблицКлюча);
	Контекст.Вставить("УчастникиГруппДоступа",                 ПараметрыОбновления.УчастникиГруппДоступа);
	Контекст.Вставить("ГруппыПользователейГруппДоступа",       ПараметрыОбновления.ГруппыПользователейГруппДоступа);
	Контекст.Вставить("ГруппыПользователейКакЗначенияДоступа", ПараметрыОбновления.ГруппыПользователейКакЗначенияДоступа);
	Контекст.Вставить("ПользователиГруппПользователей",        ПараметрыОбновления.ПользователиГруппПользователей);
	Контекст.Вставить("ПраваНаСпискиВедущихКлючейДоступа",     ПараметрыОбновления.ПраваНаСпискиВедущихКлючейДоступа);
	Контекст.Вставить("ПраваНаВедущиеКлючиДоступа",            ПараметрыОбновления.ПраваНаВедущиеКлючиДоступа);
	Контекст.Вставить("ПраваНаВедущиеСписки",                  ПараметрыОбновления.ПраваНаВедущиеСписки);
	Контекст.Вставить("ПраваПоВладельцамНастроекПрав",         ПараметрыОбновления.ПраваПоВладельцамНастроекПрав);
	Контекст.Вставить("РассчитыватьПраваПользователей",        ПараметрыОбновления.РассчитыватьПраваПользователей);
	Контекст.Вставить("ТипГруппыДоступа",                      ПараметрыОбновления.ТипГруппыДоступа);
	Контекст.Вставить("ПустаяГруппаДоступа",                   ПараметрыОбновления.ПустаяГруппаДоступа);
	Контекст.Вставить("ТипПользователя",                       ПараметрыОбновления.ТипПользователя);
	Контекст.Вставить("ТипГруппыПользователей",                ПараметрыОбновления.ТипГруппыПользователей);
	Контекст.Вставить("ТипыВладельцевНастроекПрав",            ПараметрыОбновления.ТипыВладельцевНастроекПрав);
	Контекст.Вставить("ПраваРолейФункцииПравоДоступа",         ПараметрыОбновления.Кэш.ПраваРолейФункцииПравоДоступа);
	Контекст.Вставить("ОбъектыМетаданныхФункцииПравоДоступа",  ПараметрыОбновления.Кэш.ОбъектыМетаданныхФункцииПравоДоступа);
	Контекст.Вставить("ПраваПрофилейФункцииПравоДоступа",      ПараметрыОбновления.Кэш.ПраваПрофилейФункцииПравоДоступа);
	Контекст.Вставить("РолиПрофилейГруппДоступа",              ПараметрыОбновления.РолиПрофилейГруппДоступа);
	Контекст.Вставить("ГруппыДоступаПрофилей",                 ПараметрыОбновления.ГруппыДоступаПрофилей);
	
	ЧтениеРазрешеноДляВсехГруппДоступа = Истина;
	ИзменениеРазрешеноДляВсехГруппДоступа = Истина;
	ДобавлениеРазрешеноДляВсехГруппДоступа = Истина;
	
	Для Каждого ОписаниеПрав Из ПараметрыОбновления.ПраваГруппДоступаСписка Цикл
		ГруппаДоступа      = ОписаниеПрав.ГруппаДоступа;
		ПраваГруппыДоступа = ОписаниеПрав; // СтрокаТаблицыЗначений
		
		Контекст.Вставить("ГруппаДоступа", ГруппаДоступа);
		Контекст.Вставить("ЗначенияГруппыДоступа",
			ПараметрыОбновления.ЗначенияГруппДоступа.Получить(ГруппаДоступа));
		
		Если БезЗаписиПраваЧтение
		 Или ПраваГруппыДоступа.ПравоЧтениеБезОграничения Тогда
			
			ПравоЧтение = "Истина";
		Иначе
			ПравоЧтение = РассчитанноеУсловиеДляСтрок(Контекст,
				ПараметрыОбновления.СтруктураРасчетаПраваЧтение);
		КонецЕсли;
		
		Если ПравоЧтение <> "Истина" Тогда
			ЧтениеРазрешеноДляВсехГруппДоступа = Ложь;
			ИзменениеРазрешеноДляВсехГруппДоступа = Ложь;
			ДобавлениеРазрешеноДляВсехГруппДоступа = Ложь;
		КонецЕсли;
		
		Если ПравоЧтение = "Ложь"
		 Или ТипЗнч(ПравоЧтение) = Тип("Соответствие")
		   И ПравоЧтение.Количество() = 0 Тогда
			
			Продолжить;
		КонецЕсли;
		
		Если ПраваГруппыДоступа.ПравоИзменениеБезОграничения
		   И ПраваГруппыДоступа.ПравоДобавлениеБезОграничения Тогда
			
			ПравоИзменение = "Истина";
			
		ИначеЕсли Не ПраваГруппыДоступа.ПравоИзменение Тогда
			ПравоИзменение = "Ложь";
			
		ИначеЕсли ПараметрыОбновления.ЕстьОграничениеИзменения Тогда
			ПравоИзменение = РассчитанноеУсловиеДляСтрок(Контекст,
				ПараметрыОбновления.СтруктураРасчетаПраваИзменение);
			
		ИначеЕсли БезЗаписиПраваЧтение
		      Или ПраваГруппыДоступа.ПравоЧтениеБезОграничения Тогда
			
			ПравоИзменение = РассчитанноеУсловиеДляСтрок(Контекст,
				ПараметрыОбновления.СтруктураРасчетаПраваЧтение);
		Иначе
			ПравоИзменение = "Истина";
		КонецЕсли;
		
		ПравоДобавление = ?(ПраваГруппыДоступа.ПравоДобавление, ПравоИзменение, "Ложь");
		
		Если ПраваГруппыДоступа.ПравоИзменениеБезОграничения Тогда
			ПравоИзменение = "Истина";
		КонецЕсли;
		
		Если ПравоИзменение <> "Истина" Тогда
			ИзменениеРазрешеноДляВсехГруппДоступа = Ложь;
		КонецЕсли;
		Если ПравоДобавление <> "Истина" Тогда
			ДобавлениеРазрешеноДляВсехГруппДоступа = Ложь;
		КонецЕсли;
		
		Если Контекст.РассчитыватьПраваПользователей Тогда
			ДобавитьПраваПользователейНаКлючДоступа(ПраваНаКлюч,
				ПравоЧтение, ПравоИзменение, ПравоДобавление, Контекст);
		
		ИначеЕсли Не БезЗаписиПраваЧтение Или ПравоИзменение = "Истина" Или ПравоДобавление = "Истина" Тогда
			ПраваНаКлюч.ДляГрупп.Вставить(ГруппаДоступа, Новый Структура("ПравоИзменение, ПравоДобавление",
				ПравоИзменение = "Истина", ПравоДобавление = "Истина"));
		КонецЕсли;
	КонецЦикла;
	
	Если Не ЧтениеРазрешеноДляВсехГруппДоступа
	 Или ПараметрыОбновления.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа
	   И Не ПараметрыОбновления.ЧтениеРазрешеноДляВсехПользователей Тогда
		
		Возврат ПраваНаКлюч;
	КонецЕсли;
	
	Если ДобавлениеРазрешеноДляВсехГруппДоступа
	   И ( Не ПараметрыОбновления.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа
	      Или ПараметрыОбновления.ИзменениеРазрешеноДляВсехПользователей) Тогда
		
		ПраваНаКлюч = Новый Структура("ДляГрупп, ДляПользователей", Новый Соответствие, Новый Соответствие);
		ПраваНаКлюч.ДляГрупп.Вставить(ПараметрыОбновления.ПустаяГруппаДоступа,
			Новый Структура("ПравоИзменение, ПравоДобавление", Истина, Истина));
		
	Иначе // ЧтениеРазрешеноДляВсехГруппДоступа.
		ТекущиеПраваНаКлюч = ПраваНаКлюч;
		ПраваНаКлюч = Новый Структура("ДляГрупп, ДляПользователей", Новый Соответствие, Новый Соответствие);
		Если Не БезЗаписиПраваЧтение Или ИзменениеРазрешеноДляВсехГруппДоступа Тогда
			ПраваНаКлюч.ДляГрупп.Вставить(ПараметрыОбновления.ПустаяГруппаДоступа,
				Новый Структура("ПравоИзменение, ПравоДобавление", ИзменениеРазрешеноДляВсехГруппДоступа, Ложь));
		КонецЕсли;
		ИмяПрава = ?(ИзменениеРазрешеноДляВсехГруппДоступа, "ПравоДобавление", "ПравоИзменение");
		Для Каждого КлючИЗначение Из ТекущиеПраваНаКлюч.ДляГрупп Цикл
			Если КлючИЗначение.Значение[ИмяПрава] Тогда
				ПраваНаКлюч.ДляГрупп.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
			КонецЕсли;
		КонецЦикла;
		Для Каждого КлючИЗначение Из ТекущиеПраваНаКлюч.ДляПользователей Цикл
			Если КлючИЗначение.Значение[ИмяПрава] Тогда
				ПраваНаКлюч.ДляПользователей.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ПраваНаКлюч;
	
КонецФункции

// Для процедуры ПраваНаКлючДоступаСписка.
Процедура ДобавитьПраваПользователейНаКлючДоступа(ПраваНаКлюч, ПравоЧтение, ПравоИзменение, ПравоДобавление, Контекст)
	
	Если ТипЗнч(ПравоИзменение) = Тип("Соответствие") И ПравоИзменение.Количество() = 0 Тогда
		ПравоИзменение  = "Ложь";
		ПравоДобавление = "Ложь";
		
	ИначеЕсли ТипЗнч(ПравоДобавление) = Тип("Соответствие") И ПравоДобавление.Количество() = 0 Тогда
		ПравоДобавление = "Ложь";
	КонецЕсли;
	
	Права = НовыеПрава(ПравоИзменение = "Истина", ПравоДобавление = "Истина");
	
	Если ТипЗнч(ПравоЧтение) <> Тип("Соответствие") Тогда
		Если Не Контекст.БезЗаписиПраваЧтение Или Права.ПравоИзменение Или Права.ПравоДобавление Тогда
			ПраваНаКлюч.ДляГрупп.Вставить(Контекст.ГруппаДоступа,
				Новый Структура("ПравоИзменение, ПравоДобавление",
					Права.ПравоИзменение, Права.ПравоДобавление));
		КонецЕсли;
	Иначе
		ДобавитьПользователямПраваНаКлючДоступа(ПраваНаКлюч.ДляПользователей,
			ПравоЧтение, Права.ПравоИзменение, Права.ПравоДобавление, Контекст.БезЗаписиПраваЧтение);
	КонецЕсли;
	
	Если ТипЗнч(ПравоИзменение)  <> Тип("Соответствие")
	   И ТипЗнч(ПравоДобавление) <> Тип("Соответствие") Тогда
		Возврат;
	КонецЕсли;
	
	Право = ?(ТипЗнч(ПравоИзменение) = Тип("Соответствие"), ПравоИзменение, ПравоДобавление);
	ДобавитьПользователямПраваНаКлючДоступа(ПраваНаКлюч.ДляПользователей,
		Право, Истина, ПравоДобавление <> "Ложь", Контекст.БезЗаписиПраваЧтение);
	
КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//     * ПравоИзменение  - Булево
//     * ПравоДобавление - Булево
//
Функция НовыеПрава(ПравоИзменение, ПравоДобавление)
	
	Возврат Новый Структура("ПравоИзменение, ПравоДобавление", ПравоИзменение, ПравоДобавление);
	
КонецФункции

// Для процедуры ДобавитьПраваПользователейНаКлючДоступа.
Процедура ДобавитьПользователямПраваНаКлючДоступа(ПраваНаКлючДляПользователей,
				СоставПользователей, ПравоИзменение, ПравоДобавление, БезЗаписиПраваЧтение)
	
	Если ПравоИзменение И ПравоДобавление Тогда
		Для Каждого КлючИЗначение Из СоставПользователей Цикл
			ПраваНаКлючДляПользователей.Вставить(КлючИЗначение.Ключ,
				Новый Структура("ПравоИзменение, ПравоДобавление", Истина, Истина));
		КонецЦикла;
		
	ИначеЕсли Не ПравоИзменение И Не ПравоДобавление Тогда
		Если Не БезЗаписиПраваЧтение Тогда
			Для Каждого КлючИЗначение Из СоставПользователей Цикл
				Права = ПраваНаКлючДляПользователей.Получить(КлючИЗначение.Ключ);
				Если Права = Неопределено Тогда
					Права = Новый Структура("ПравоИзменение, ПравоДобавление", Ложь, Ложь);
					ПраваНаКлючДляПользователей.Вставить(КлючИЗначение.Ключ, Права);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
	Иначе
		Для Каждого КлючИЗначение Из СоставПользователей Цикл
			Права = ПраваНаКлючДляПользователей.Получить(КлючИЗначение.Ключ); // См. НовыеПрава
			Если Права = Неопределено Тогда
				Права = Новый Структура("ПравоИзменение, ПравоДобавление",
					ПравоИзменение, ПравоДобавление);
			Иначе
				Права.ПравоИзменение  = Права.ПравоИзменение  Или ПравоИзменение;
				Права.ПравоДобавление = Права.ПравоДобавление Или ПравоДобавление;
			КонецЕсли;
			ПраваНаКлючДляПользователей.Вставить(КлючИЗначение.Ключ, Права);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Для функций ПраваНаКлючДоступаСписка и РассчитанноеУсловие.
Функция РассчитанноеУсловиеДляСтрок(Контекст, Условие, УзелРеквизитов = Неопределено, ДляЛюбойИзСтрок = Истина, КорневойУзел = Истина)
	
	Если УзелРеквизитов = Неопределено Тогда
		Контекст.Вставить("ОписанияТребуемыхТаблицКлюча", Новый Массив);
		Если Условие.Узел = "ДляВсехСтрок" Или Условие.Узел = "ДляОднойИзСтрок" Тогда
			Возврат РассчитанноеУсловие(Контекст, Условие, КорневойУзел);
		КонецЕсли;
		УзелРеквизитов = Условие;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(УзелРеквизитов.ТребуемыеРеквизитыТабличныхЧастейКлюча) Тогда
		Контекст.Вставить("ТекущиеСтрокиТаблицКлюча", Контекст.ЗначенияТаблицКлюча);
		Возврат РассчитанноеУсловие(Контекст, Условие, КорневойУзел);
	КонецЕсли;
	РеквизитыТаблиц    = УзелРеквизитов.ТребуемыеРеквизитыТабличныхЧастейКлюча;
	ЗначенияТаблиц     = ТекущиеЗначенияТаблицКлюча(Контекст, РеквизитыТаблиц);
	ИндексыСтрокТаблиц = Новый Соответствие;
	
	Контекст.ОписанияТребуемыхТаблицКлюча.Добавить(
		Новый Структура("РеквизитыТаблиц, ЗначенияТаблиц, ИндексыСтрокТаблиц",
			РеквизитыТаблиц, ЗначенияТаблиц, ИндексыСтрокТаблиц));
	
	ТекущиеСтрокиТаблицКлюча = ?(Контекст.Свойство("ТекущиеСтрокиТаблицКлюча"),
		Контекст.ТекущиеСтрокиТаблицКлюча, Неопределено);
	
	Контекст.Вставить("ТекущиеСтрокиТаблицКлюча", Новый Структура);
	Для Каждого ОписаниеЗначений Из Контекст.ЗначенияТаблицКлюча Цикл
		Если СтрНачинаетсяС(ОписаниеЗначений.Ключ, "Шапка") Тогда
			Контекст.ТекущиеСтрокиТаблицКлюча.Вставить(ОписаниеЗначений.Ключ, ОписаниеЗначений.Значение);
		КонецЕсли;
	КонецЦикла;
	
	Результат = "Неопределено";
	
	Пока Истина Цикл
		ИндексИзменен = Ложь;
		Для Каждого ОписаниеТаблицы Из РеквизитыТаблиц Цикл
			ИмяТаблицыКлюча = ОписаниеТаблицы.Ключ;
			ИндексСтроки = ИндексыСтрокТаблиц.Получить(ИмяТаблицыКлюча);
			ЗначенияТаблицы = ЗначенияТаблиц[ИмяТаблицыКлюча];
			Если ИндексСтроки = Неопределено Тогда
				ИндексСтроки = 0;
			Иначе
				Если ИндексСтроки >= ЗначенияТаблицы.Количество() - 1 Тогда
					Продолжить;
				КонецЕсли;
				ИндексСтроки = ИндексСтроки + 1;
			КонецЕсли;
			ИндексИзменен = Истина;
			ИндексыСтрокТаблиц.Вставить(ИмяТаблицыКлюча, ИндексСтроки);
			Контекст.ТекущиеСтрокиТаблицКлюча.Вставить(ИмяТаблицыКлюча, ЗначенияТаблицы[ИндексСтроки]);
		КонецЦикла;
		Если Не ИндексИзменен Тогда
			Прервать;
		КонецЕсли; 
		ТекущийРезультат = РассчитанноеУсловие(Контекст, Условие);
		Если ТипЗнч(ТекущийРезультат) <> Тип("Соответствие") Тогда
			Если ДляЛюбойИзСтрок Тогда
				Если ТекущийРезультат = "Истина" Тогда
					Результат = "Истина";
					Прервать;
				ИначеЕсли ТекущийРезультат = "Ложь" Тогда
					Если Результат = "Неопределено" Или Результат = "Пусто" Тогда
						Результат = "Ложь";
					КонецЕсли;
				ИначеЕсли ТекущийРезультат = "Пусто" Тогда
					Если Результат = "Неопределено" Тогда
						Результат = "Пусто";
					КонецЕсли;
				КонецЕсли;
			Иначе
				Если ТекущийРезультат = "Ложь" Тогда
					Результат = "Ложь";
					Прервать;
				ИначеЕсли ТекущийРезультат = "Истина" Тогда
					Если Результат = "Неопределено" Или Результат = "Пусто" Тогда
						Результат = "Истина";
					КонецЕсли;
				ИначеЕсли ТекущийРезультат = "Пусто" Тогда
					Если Результат = "Неопределено" Тогда
						Результат = "Пусто";
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
		Иначе
			Если Не ДляЛюбойИзСтрок И ТекущийРезультат.Количество() = 0 Тогда
				Результат = "Ложь";
				Прервать;
			КонецЕсли;
			Если ТипЗнч(Результат) = Тип("Строка") Тогда
				Результат = ТекущийРезультат;
			Иначе
				ДобавитьТекущийРезультат(Результат, ТекущийРезультат, ДляЛюбойИзСтрок, Контекст);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Если Результат = "Неопределено" Тогда
		Результат = "Ложь";
	ИначеЕсли КорневойУзел И Результат = "Пусто" Тогда
		Результат = "Истина";
	КонецЕсли;
	
	Контекст.ОписанияТребуемыхТаблицКлюча.Удалить(
		Контекст.ОписанияТребуемыхТаблицКлюча.Количество() - 1);
	
	Контекст.Вставить("ТекущиеСтрокиТаблицКлюча", ТекущиеСтрокиТаблицКлюча);
	
	Возврат Результат;
	
КонецФункции

// Для функции РассчитанноеУсловиеДляСтрок.
Процедура ДобавитьТекущийРезультат(Результат, ТекущийРезультат, ДляЛюбойИзСтрок, Контекст)
	
	Если ДляЛюбойИзСтрок Тогда
		Для Каждого КлючИЗначение Из ТекущийРезультат Цикл
			Результат.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		КонецЦикла;
	Иначе
		Если Результат.Количество() > ТекущийРезультат.Количество() Тогда
			МеньшийРезультат = ТекущийРезультат;
			БольшийРезультат = Результат;
		Иначе
			МеньшийРезультат = Результат;
			БольшийРезультат = ТекущийРезультат;
		КонецЕсли;
		Результат = Новый Соответствие;
		ТипГруппыПользователей = Неопределено;
		
		Для Каждого КлючИЗначение Из МеньшийРезультат Цикл
			Участник = КлючИЗначение.Ключ;
			Если БольшийРезультат.Получить(Участник) <> Неопределено Тогда
				Результат.Вставить(Участник, Истина);
			Иначе
				Если ТипГруппыПользователей = Неопределено Тогда
					ТипГруппыПользователей = Контекст.ТипГруппыПользователей;
					ПользователиГруппПользователей = Контекст.ПользователиГруппПользователей;
					БольшийРезультатПользователиГрупп = ПользователиГрупп(БольшийРезультат, Контекст);
				КонецЕсли;
				Если ТипЗнч(Участник) = ТипГруппыПользователей Тогда
					ПользователиГруппы = ПользователиГруппПользователей.Получить(Участник);
					Если ПользователиГруппы = Неопределено Тогда
						Продолжить;
					КонецЕсли;
					Для Каждого ОписаниеПользователя Из ПользователиГруппы Цикл
						Если БольшийРезультатПользователиГрупп.Получить(ОписаниеПользователя.Ключ) <> Неопределено Тогда
							Результат.Вставить(ОписаниеПользователя.Ключ, Истина);
						КонецЕсли;
					КонецЦикла;
				ИначеЕсли БольшийРезультатПользователиГрупп.Получить(Участник) <> Неопределено Тогда
					Результат.Вставить(Участник, Истина);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ДобавитьТекущийРезультат и УстановитьОбратныйРезультат.
Функция ПользователиГрупп(ПользователиИГруппы, Контекст)
	
	ПользователиГрупп = Новый Соответствие;
	
	ТипГруппыПользователей = Контекст.ТипГруппыПользователей;
	ПользователиГруппПользователей = Контекст.ПользователиГруппПользователей;
	
	Для Каждого КлючИЗначение Из ПользователиИГруппы Цикл
		Если ТипЗнч(КлючИЗначение.Ключ) = ТипГруппыПользователей Тогда
			ПользователиГруппы = ПользователиГруппПользователей.Получить(КлючИЗначение.Ключ);
			Если ПользователиГруппы <> Неопределено Тогда
				Для Каждого ОписаниеПользователя Из ПользователиГруппы Цикл
					ПользователиГрупп.Вставить(ОписаниеПользователя.Ключ, Истина);
				КонецЦикла;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Возврат ПользователиГрупп;
	
КонецФункции

// Для функции РассчитанноеУсловие.
Процедура УстановитьОбратныйРезультат(Результат, Контекст)
	
	СписокИсключений = Результат;
	Результат = Новый Соответствие;
	
	УчастникиТекущейГруппыДоступа = Контекст.УчастникиГруппДоступа.Получить(Контекст.ГруппаДоступа);
	Если УчастникиТекущейГруппыДоступа = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ПользователиГруппПользователей = Контекст.ПользователиГруппПользователей;
	ТипГруппыПользователей         = Контекст.ТипГруппыПользователей;
	ТипПользователя                = Контекст.ТипПользователя;
	
	ПользователиИсключаемыхГрупп = ПользователиГрупп(СписокИсключений, Контекст);
	
	Для Каждого КлючИЗначение Из УчастникиТекущейГруппыДоступа Цикл
		Участник = КлючИЗначение.Ключ;
		Если СписокИсключений.Получить(Участник) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Если ТипЗнч(Участник) = ТипПользователя Тогда
			Если СписокИсключений.Получить(Участник) = Неопределено
			   И ПользователиИсключаемыхГрупп.Получить(Участник) = Неопределено Тогда
				
				Результат.Вставить(Участник, Истина);
			КонецЕсли;
		ИначеЕсли ТипЗнч(Участник) = ТипГруппыПользователей Тогда
			ПользователиГруппы = ПользователиГруппПользователей.Получить(Участник);
			Если ПользователиГруппы = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ВсяГруппаБезИсключений = Истина;
			Для Каждого ОписаниеПользователя Из ПользователиГруппы Цикл
				Если СписокИсключений.Получить(ОписаниеПользователя.Ключ) <> Неопределено
				 Или ПользователиИсключаемыхГрупп.Получить(ОписаниеПользователя.Ключ) <> Неопределено Тогда
					
					ВсяГруппаБезИсключений = Ложь;
					Прервать;
				КонецЕсли;
			КонецЦикла;
			Если ВсяГруппаБезИсключений Тогда
				Результат.Вставить(Участник, Истина);
			Иначе
				Для Каждого ОписаниеПользователя Из ПользователиГруппы Цикл
					Если СписокИсключений.Получить(ОписаниеПользователя.Ключ) = Неопределено
					   И ПользователиИсключаемыхГрупп.Получить(ОписаниеПользователя.Ключ) = Неопределено Тогда
						
						Результат.Вставить(ОписаниеПользователя.Ключ, Истина);
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для функции РассчитанноеУсловиеДляСтрок.
//
// Возвращаемое значение:
//  Структура из КлючИЗначение:
//   * Ключ - Строка - имя таблицы ключа
//   * Значение - см. ЗначенияТаблицыКлюча
//
Функция ТекущиеЗначенияТаблицКлюча(Контекст, ТребуемыеРеквизитыТабличныхЧастейКлюча)
	
	ТекущиеЗначенияТаблицКлюча = Новый Структура;
	Для Каждого ОписаниеТаблицы Из ТребуемыеРеквизитыТабличныхЧастейКлюча Цикл
		ИмяТаблицыКлюча = ОписаниеТаблицы.Ключ;
		Реквизиты       = ОписаниеТаблицы.Значение;
		ЗначенияТаблицыКлюча = Контекст.ЗначенияТаблицКлюча[ИмяТаблицыКлюча];
		Отбор = Новый Структура;
		Для Каждого ТекущееОписание Из Контекст.ОписанияТребуемыхТаблицКлюча Цикл
			РеквизитыТаблицы    = ТекущееОписание.РеквизитыТаблиц.Получить(ИмяТаблицыКлюча);
			ЗначенияТаблицы     = ТекущееОписание.ЗначенияТаблиц[ИмяТаблицыКлюча];
			ИндексСтрокиТаблицы = ТекущееОписание.ИндексыСтрокТаблиц.Получить(ИмяТаблицыКлюча);
			СтрокаТаблицы = ЗначенияТаблицы[ИндексСтрокиТаблицы];
			Для Каждого ИмяРеквизита Из РеквизитыТаблицы Цикл
				Отбор.Вставить(ИмяРеквизита, СтрокаТаблицы[ИмяРеквизита]);
			КонецЦикла;
		КонецЦикла;
		ВсегоРеквизитовТаблицы = Контекст.РеквизитыТаблицКлюча.Получить(ИмяТаблицыКлюча).Количество();
		Если ВсегоРеквизитовТаблицы = Реквизиты.Количество() И Не ЗначениеЗаполнено(Отбор) Тогда
			ТекущиеЗначенияТаблицКлюча.Вставить(ИмяТаблицыКлюча, ЗначенияТаблицыКлюча);
			Продолжить;
		КонецЕсли;
		Строки = ЗначенияТаблицыКлюча;
		Если ЗначениеЗаполнено(Отбор) Тогда
			Строки = Строки.НайтиСтроки(Отбор);
		КонецЕсли;
		ЗначенияТаблицы = НовыеЗначенияТаблицыКлюча();
		Для Каждого ИмяРеквизита Из Реквизиты Цикл
			ЗначенияТаблицы.Колонки.Добавить(ИмяРеквизита);
		КонецЦикла;
		Для Каждого Строка Из Строки Цикл
			ЗаполнитьЗначенияСвойств(ЗначенияТаблицы.Добавить(), Строка);
		КонецЦикла;
		Если Реквизиты.Количество() < ВсегоРеквизитовТаблицы Тогда
			РеквизитыСтрокой = СтрСоединить(Реквизиты, ", ");
			ЗначенияТаблицы.Свернуть(РеквизитыСтрокой);
		КонецЕсли;
		ТекущиеЗначенияТаблицКлюча.Вставить(ИмяТаблицыКлюча, ЗначенияТаблицы);
	КонецЦикла;
	
	Возврат ТекущиеЗначенияТаблицКлюча;
	
КонецФункции

// Для функции РассчитанноеУсловиеДляСтрок.
Функция РассчитанноеУсловие(Контекст, Условие, КорневойУзел = Ложь)
	
	// Проверяемые типы уже учтены.
	
	Если Условие.Узел = "Поле" Тогда
		Значение = Контекст.ТекущиеСтрокиТаблицКлюча[Условие.Таблица][Условие.Реквизит];
		Результат = ?(Значение = Перечисления.ДополнительныеЗначенияДоступа.Истина
			Или Значение = Null И Условие.Свойство("ПроверкаЕстьNull"), "Истина", "Ложь");
		
	ИначеЕсли Условие.Узел = "Константа" Тогда
		Результат = ?(Условие.Значение = Истина, "Истина",
			?(Условие.Значение = "Пусто", "Пусто", "Ложь"));
		
	ИначеЕсли Условие.Узел = "И" Тогда
		
		Результат = "Неопределено";
		Для Каждого Аргумент Из Условие.Аргументы Цикл
			ТекущийРезультат = РассчитанноеУсловие(Контекст, Аргумент);
			Если ТекущийРезультат = "Ложь" Тогда
				Результат = "Ложь";
				Прервать;
			КонецЕсли;
			Если ТекущийРезультат = "Истина" Тогда
				Если Результат = "Неопределено" Или Результат = "Пусто" Тогда
					Результат = "Истина";
				КонецЕсли;
			ИначеЕсли ТекущийРезультат = "Пусто" Тогда
				Если Результат = "Неопределено" Тогда
					Результат = "Пусто";
				КонецЕсли;
			ИначеЕсли ТипЗнч(ТекущийРезультат) = Тип("Соответствие") Тогда
				Если ТекущийРезультат.Количество() = 0 Тогда
					Результат = "Ложь";
					Прервать;
				КонецЕсли;
				Если ТипЗнч(Результат) = Тип("Строка") Тогда
					Результат = ТекущийРезультат;
				Иначе
					ДобавитьТекущийРезультат(Результат, ТекущийРезультат, Ложь, Контекст);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Если Результат = "Неопределено" Тогда
			Результат = "Ложь";
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "Или" Тогда
		
		Результат = "Неопределено";
		Для Каждого Аргумент Из Условие.Аргументы Цикл
			ТекущийРезультат = РассчитанноеУсловие(Контекст, Аргумент);
			Если ТекущийРезультат = "Истина" Тогда
				Результат = "Истина";
				Прервать;
			КонецЕсли;
			Если ТекущийРезультат = "Ложь" Тогда
				Если Результат = "Неопределено" Или Результат = "Пусто" Тогда
					Результат = "Ложь";
				КонецЕсли;
			ИначеЕсли ТекущийРезультат = "Пусто" Тогда
				Если Результат = "Неопределено" Тогда
					Результат = "Пусто";
				КонецЕсли;
			ИначеЕсли ТипЗнч(ТекущийРезультат) = Тип("Соответствие") Тогда
				Если ТекущийРезультат.Количество() = 0 Тогда
					Если Результат = "Неопределено" Тогда
						Результат = "Ложь";
					КонецЕсли;
				ИначеЕсли ТипЗнч(Результат) = Тип("Строка") Тогда
					Результат = ТекущийРезультат;
				Иначе
					ДобавитьТекущийРезультат(Результат, ТекущийРезультат, Истина, Контекст);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Если Результат = "Неопределено" Тогда
			Результат = "Ложь";
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "Не" Тогда
		Результат = РассчитанноеУсловие(Контекст, Условие.Аргумент);
		Если Результат = "Истина" Тогда
			Результат = "Ложь";
		ИначеЕсли Результат = "Ложь" Тогда
			Результат = "Истина";
		ИначеЕсли ТипЗнч(Результат) = Тип("Соответствие") Тогда
			Если Результат.Количество() = 0 Тогда
				Результат = "Истина";
			Иначе
				УстановитьОбратныйРезультат(Результат, Контекст);
			КонецЕсли;
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "ДляВсехСтрок"
	      Или Условие.Узел = "ДляОднойИзСтрок" Тогда
		
		Результат = РассчитанноеУсловиеДляСтрок(Контекст,
			Условие.Аргумент, Условие, Условие.Узел = "ДляОднойИзСтрок", Ложь);
		
	ИначеЕсли Условие.Узел = "Выбор" Тогда
		Результат = Неопределено;
		Для Каждого Когда Из Условие.Когда Цикл
			Если РассчитанноеУсловие(Контекст, Когда.Условие) = "Истина" Тогда
				Результат = РассчитанноеУсловие(Контекст, Когда.Значение);
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Результат = Неопределено Тогда
			Результат = РассчитанноеУсловие(Контекст, Условие.Иначе);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "ЗначениеРазрешено"
	      Или Условие.Узел = "ЭтоАвторизованныйПользователь"
	      Или Условие.Узел = "ЧтениеОбъектаРазрешено"
	      Или Условие.Узел = "ИзменениеОбъектаРазрешено"
	      Или Условие.Узел = "ЧтениеСпискаРазрешено"
	      Или Условие.Узел = "ИзменениеСпискаРазрешено" Тогда
		
		Значение = Контекст.ТекущиеСтрокиТаблицКлюча[Условие.Поле.Таблица][Условие.Поле.Реквизит];
		Если Значение = Null Тогда
			Значение = Перечисления.ДополнительныеЗначенияДоступа.Null;
		КонецЕсли;
		ОтключеноКакЛожь = Условие.УточненияСравнения.Получить("Отключено") = "Ложь";
		
		Если Значение = Перечисления.ДополнительныеЗначенияДоступа.ТипРазрешенный Тогда
			Если ОтключеноКакЛожь Тогда
				ТекущийРезультат = "Пусто";
			Иначе
				ТекущийРезультат = "Истина";
			КонецЕсли;
			
		ИначеЕсли Значение = Перечисления.ДополнительныеЗначенияДоступа.ТипЗапрещенный Тогда
			Если ОтключеноКакЛожь Тогда
				ТекущийРезультат = "Пусто";
			Иначе
				ТекущийРезультат = "Ложь";
			КонецЕсли;
		Иначе
			ТекущийРезультат = Неопределено;
			Для Каждого УточнениеСравнения Из Условие.УточненияСравнения Цикл
				Если УточнениеСравнения.Ключ = "Null"
				 Или УточнениеСравнения.Ключ = "Неопределено" Тогда
					Если Значение = Перечисления.ДополнительныеЗначенияДоступа[УточнениеСравнения.Ключ] Тогда
						ТекущийРезультат = УточнениеСравнения.Значение;
						Прервать;
					КонецЕсли;
				ИначеЕсли УточнениеСравнения.Ключ = "ПустаяСсылка" Тогда
					Если Значение = Перечисления.ДополнительныеЗначенияДоступа.ПустаяСсылкаЛюбогоТипа
					 Или Значение.Пустая() Тогда
						ТекущийРезультат = УточнениеСравнения.Значение;
						Прервать;
					КонецЕсли;
				ИначеЕсли УточнениеСравнения.Ключ = ТипЗнч(Значение) Тогда
					ТекущийРезультат = УточнениеСравнения.Значение;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		Если ТекущийРезультат <> Неопределено Тогда
			Результат = ТекущийРезультат;
			
		ИначеЕсли Условие.Узел = "ЗначениеРазрешено" Тогда
			Если Значение = Перечисления.ДополнительныеЗначенияДоступа.Null
			 Или Значение = Перечисления.ДополнительныеЗначенияДоступа.Неопределено Тогда
				Если ОтключеноКакЛожь Тогда
					Результат = "Пусто";
				Иначе
					Результат = "Ложь";
				КонецЕсли;
			Иначе
				Если Контекст.ЗначенияГруппыДоступа = Неопределено Тогда
					ЗначенияОдногоТипа = Неопределено;
				Иначе
					ТипЗначения = ТипЗнч(Значение);
					ЗначенияОдногоТипа = Контекст.ЗначенияГруппыДоступа.Получить(ТипЗначения);
				КонецЕсли;
				Если ЗначенияОдногоТипа = Неопределено Тогда
					Если ОтключеноКакЛожь Тогда
						Результат = "Пусто";
					Иначе
						Результат = "Истина";
					КонецЕсли;
				Иначе
					ЗначениеУказано = ЗначенияОдногоТипа.Значения.Получить(Значение) <> Неопределено;
					Если Не ЗначениеУказано И ТипЗначения = Контекст.ТипПользователя Тогда
						ЗначениеУказано = ЗначениеУказаноВГруппеПользователей(Значение, Контекст);
					КонецЕсли;
					Результат = "Ложь";
					Если    ЗначениеУказано И Не ЗначенияОдногоТипа.ВсеРазрешены
					 Или Не ЗначениеУказано И    ЗначенияОдногоТипа.ВсеРазрешены Тогда
						Результат = "Истина";
						
					ИначеЕсли ТипЗначения = Контекст.ТипПользователя Тогда
						ЗаполнитьРезультатДляПользователя(Результат, Значение, Контекст);
						
					ИначеЕсли ТипЗначения = Контекст.ТипГруппыПользователей Тогда
						ЗаполнитьРезультатДляГруппыПользователей(Результат, Значение, Контекст);
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
			
		ИначеЕсли Условие.Узел = "ЭтоАвторизованныйПользователь" Тогда
			Если ТипЗнч(Значение) = Контекст.ТипПользователя Тогда
				ЗаполнитьРезультатДляПользователя(Результат, Значение, Контекст);
			Иначе
				Результат = "Ложь";
			КонецЕсли;
			
		ИначеЕсли ТипЗнч(Значение) = Тип("СправочникСсылка.КлючиДоступа") Тогда
			ПраваНаСписок = Контекст.ПраваНаСпискиВедущихКлючейДоступа.Получить(Значение);
			Если ПраваНаСписок = Неопределено Тогда
				Результат = "Ложь";
			Иначе
				ПравоИзменение = ПраваНаСписок.ПоГруппамДоступа.Получить(Контекст.ГруппаДоступа);
				Если Условие.Узел =    "ЧтениеОбъектаРазрешено" И ПравоИзменение = Неопределено
				 Или Условие.Узел = "ИзменениеОбъектаРазрешено" И ПравоИзменение <> Истина Тогда
					Результат = "Ложь";
				
				ИначеЕсли Условие.Узел =    "ЧтениеОбъектаРазрешено" И ПраваНаСписок.ОграничениеЧтенияОтключено
					  Или Условие.Узел = "ИзменениеОбъектаРазрешено" И ПраваНаСписок.ОграничениеОтключено Тогда
					
					Результат = "Истина";
				Иначе
					ПраваНаВедущийКлючДоступа = Контекст.ПраваНаВедущиеКлючиДоступа.Получить(Значение);
					Если ПраваНаВедущийКлючДоступа = Неопределено Тогда
						Результат = "Ложь";
						
					ИначеЕсли Контекст.РассчитыватьПраваПользователей Тогда
						Если Условие.Узел = "ЧтениеОбъектаРазрешено"
						 Или Условие.Узел = "ИзменениеОбъектаРазрешено" Тогда
							
							Результат = Новый Соответствие;
							ПроверятьПравоИзменение = Условие.Узел <> "ЧтениеОбъектаРазрешено";
							Для Каждого КлючИЗначение Из ПраваНаВедущийКлючДоступа Цикл
								Если ПроверятьПравоИзменение И Не КлючИЗначение.Значение Тогда
									Продолжить;
								КонецЕсли;
								Если ТипЗнч(КлючИЗначение.Ключ) <> Контекст.ТипГруппыДоступа Тогда
									Результат.Вставить(КлючИЗначение.Ключ, Истина);
									
								ИначеЕсли КлючИЗначение.Ключ = Контекст.ГруппаДоступа Тогда
									УчастникиГруппыДоступа = Контекст.УчастникиГруппДоступа.Получить(
										Контекст.ГруппаДоступа);
									Если УчастникиГруппыДоступа <> Неопределено Тогда
										Если Результат.Количество() = 0 Тогда
											Результат = Новый Соответствие(
												Новый ФиксированноеСоответствие(УчастникиГруппыДоступа));
										Иначе
											Для Каждого ОписаниеУчастника Из УчастникиГруппыДоступа Цикл
												Результат.Вставить(ОписаниеУчастника.Ключ, Истина);
											КонецЦикла;
										КонецЕсли;
									КонецЕсли;
								ИначеЕсли КлючИЗначение.Ключ = Контекст.ПустаяГруппаДоступа Тогда
									Результат = "Истина";
									Прервать;
								КонецЕсли;
							КонецЦикла;
						Иначе
							Результат = "Ложь";
						КонецЕсли;
					Иначе
						ПравоИзменение = ПраваНаВедущийКлючДоступа.Получить(Контекст.ГруппаДоступа);
						Если Условие.Узел =    "ЧтениеОбъектаРазрешено" И ПравоИзменение <> Неопределено
						 Или Условие.Узел = "ИзменениеОбъектаРазрешено" И ПравоИзменение = Истина Тогда
							Результат = "Истина";
						Иначе
							Результат = "Ложь";
						КонецЕсли;
					КонецЕсли;
				КонецЕсли;
			КонецЕсли;
			
		ИначеЕсли Контекст.ТипыВладельцевНастроекПрав.Получить(ТипЗнч(Значение)) <> Неопределено Тогда
			ПраваПоВладельцуНастроекПрав = Контекст.ПраваПоВладельцамНастроекПрав.Получить(Значение);
			Если ПраваПоВладельцуНастроекПрав = Неопределено Тогда
				Результат = "Ложь";
				
			ИначеЕсли Условие.Узел = "ЧтениеОбъектаРазрешено" Тогда
				Результат = Новый Соответствие(ПраваПоВладельцуНастроекПрав);
				
			ИначеЕсли Условие.Узел = "ИзменениеОбъектаРазрешено" Тогда
				Результат = Новый Соответствие;
				Для Каждого КлючИЗначение Из ПраваПоВладельцуНастроекПрав Цикл
					Если КлючИЗначение.Значение Тогда
						Результат.Вставить(КлючИЗначение.Ключ, Истина);
					КонецЕсли;
				КонецЦикла;
			Иначе
				Результат = "Ложь";
			КонецЕсли;
			
		Иначе // Проверка прав на список.
			ПраваНаВедущийСписок = Контекст.ПраваНаВедущиеСписки.Получить(Значение);
			Если ПраваНаВедущийСписок = Неопределено Тогда
				ПраваНаВедущийСписок = Контекст.ПраваНаВедущиеСписки.Получить(ТипЗнч(Значение));
			КонецЕсли;
			Если ПраваНаВедущийСписок = Неопределено Тогда
				Результат = "Ложь";
			Иначе
				ПравоИзменение = ПраваНаВедущийСписок.Получить(Контекст.ГруппаДоступа);
				Если Условие.Узел = "ЧтениеСпискаРазрешено"     И ПравоИзменение <> Неопределено
				 Или Условие.Узел = "ЧтениеОбъектаРазрешено"    И ПравоИзменение <> Неопределено
				 Или Условие.Узел = "ИзменениеСпискаРазрешено"  И ПравоИзменение = Истина
				 Или Условие.Узел = "ИзменениеОбъектаРазрешено" И ПравоИзменение = Истина Тогда
					
					Если Контекст.РассчитыватьПраваПользователей Тогда
						УчастникиГруппыДоступа = Контекст.УчастникиГруппДоступа.Получить(Контекст.ГруппаДоступа);
						Если УчастникиГруппыДоступа <> Неопределено Тогда
							Результат = Новый Соответствие(Новый ФиксированноеСоответствие(УчастникиГруппыДоступа));
						Иначе
							Результат = "Ложь";
						КонецЕсли;
					Иначе
						Результат = "Истина";
					КонецЕсли;
				Иначе
					Результат = "Ложь";
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "ПравоДоступа" Тогда
		Результат = ?(ЕстьПравоДоступаВРоляхПрофиляГруппыДоступа(Условие, Контекст), "Истина", "Ложь");
		
	ИначеЕсли Условие.Узел = "РольДоступна" Тогда
		Результат = ?(ЕстьРольВПрофилеГруппыДоступа(Условие, Контекст), "Истина", "Ложь");
		
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При вычислении прав на ключ доступа узел не поддерживается ""%1"".'"),
			Условие.Узел);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если КорневойУзел И Результат = "Пусто" Тогда
		Результат = "Истина";
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Для функции РассчитанноеУсловие.
Функция ЗначениеУказаноВГруппеПользователей(Пользователь, Контекст)
	
	УказанныеГруппыПользователей = Контекст.ЗначенияГруппыДоступа.Получить(Контекст.ТипГруппыПользователей);
	Если УказанныеГруппыПользователей = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Для Каждого УказаннаяГруппаПользователей Из УказанныеГруппыПользователей.Значения Цикл
		ПользователиГруппы = Контекст.ГруппыПользователейКакЗначенияДоступа.Получить(УказаннаяГруппаПользователей.Ключ);
		Если ПользователиГруппы = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Если ПользователиГруппы.Получить(Пользователь) <> Неопределено Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для функции РассчитанноеУсловие.
Процедура ЗаполнитьРезультатДляПользователя(Результат, Пользователь, Контекст)
	
	Результат = "Ложь";
	
	УчастникиГруппыДоступа = Контекст.УчастникиГруппДоступа.Получить(Контекст.ГруппаДоступа);
	Если УчастникиГруппыДоступа = Неопределено Тогда
		Возврат;
	КонецЕсли;
	Если УчастникиГруппыДоступа.Получить(Пользователь) <> Неопределено Тогда
		Результат = Новый Соответствие;
		Результат.Вставить(Пользователь, Истина);
		Возврат;
	КонецЕсли;
	
	ГруппыПользователейГруппыДоступа = Контекст.ГруппыПользователейГруппДоступа.Получить(Контекст.ГруппаДоступа);
	Если ГруппыПользователейГруппыДоступа = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Для Каждого ОписаниеПользователейГруппы Из ГруппыПользователейГруппыДоступа Цикл
		Если ОписаниеПользователейГруппы.Значение.Получить(Пользователь) <> Неопределено Тогда
			Результат = Новый Соответствие;
			Результат.Вставить(Пользователь, Истина);
			Возврат;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для функции РассчитанноеУсловие.
Процедура ЗаполнитьРезультатДляГруппыПользователей(Результат, ГруппаПользователей, Контекст)
	
	Результат = "Ложь";
	
	УчастникиГруппыДоступа = Контекст.УчастникиГруппДоступа.Получить(Контекст.ГруппаДоступа);
	Если УчастникиГруппыДоступа = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если УчастникиГруппыДоступа.Получить(ГруппаПользователей) <> Неопределено Тогда
		Результат = Новый Соответствие;
		Результат.Вставить(ГруппаПользователей, Истина);
		Возврат;
	КонецЕсли;
	
	ГруппыПользователейГруппыДоступа = Контекст.ГруппыПользователейГруппДоступа.Получить(Контекст.ГруппаДоступа);
	Если ГруппыПользователейГруппыДоступа = Неопределено Тогда
		ГруппыПользователейГруппыДоступа = Новый Соответствие;
	КонецЕсли;
	
	ПользователиГруппы = Контекст.ПользователиГруппПользователей.Получить(ГруппаПользователей);
	Если ПользователиГруппы = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Результат = Новый Соответствие;
	Для Каждого ОписаниеПользователя Из ПользователиГруппы Цикл
		Если УчастникиГруппыДоступа.Получить(ОписаниеПользователя.Ключ) <> Неопределено Тогда
			Результат.Вставить(ОписаниеПользователя.Ключ, Истина);
			Продолжить;
		КонецЕсли;
		Для Каждого ОписаниеПользователейГруппы Из ГруппыПользователейГруппыДоступа Цикл
			Если ОписаниеПользователейГруппы.Значение.Получить(ОписаниеПользователя.Ключ) <> Неопределено Тогда
				Результат.Вставить(ОписаниеПользователя.Ключ, Истина);
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Если Результат.Количество() = 0 Тогда
		Результат = "Ложь";
	КонецЕсли;
	
КонецПроцедуры

// Для функции РассчитанноеУсловие.
Функция ЕстьПравоДоступаВРоляхПрофиляГруппыДоступа(Условие, Контекст)
	
	ПолноеИмя = Условие.ПолноеИмяОбъектаМетаданных;
	Описание = Контекст.ОбъектыМетаданныхФункцииПравоДоступа.Получить(ПолноеИмя);
	Если Описание = Неопределено Тогда
		Описание = Новый Структура("ОбъектМетаданных, ИмяСтандартногоРеквизита");
		Описание.ОбъектМетаданных = ОбъектМетаданныхПоПолномуИмениДляПроверкиПрава(
			ПолноеИмя, Описание.ИмяСтандартногоРеквизита);
		Контекст.ОбъектыМетаданныхФункцииПравоДоступа.Вставить(ПолноеИмя, Описание);
	КонецЕсли;
	
	СтрокаТаблицы = Контекст.ГруппыДоступаПрофилей.Найти(Контекст.ГруппаДоступа, "ГруппаДоступа");
	Если СтрокаТаблицы = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ПраваПрофиля = Контекст.ПраваПрофилейФункцииПравоДоступа.Получить(СтрокаТаблицы.Профиль);
	Если ПраваПрофиля = Неопределено Тогда
		ПраваПрофиля = Новый Соответствие;
		Контекст.ПраваПрофилейФункцииПравоДоступа.Вставить(СтрокаТаблицы.Профиль, ПраваПрофиля);
	КонецЕсли;
	
	КлючПраваВРоляхПрофиля = ПолноеИмя + "/" + Условие.ИмяПрава;
	ЕстьПравоВРоляхПрофиля = ПраваПрофиля.Получить(КлючПраваВРоляхПрофиля);
	Если ТипЗнч(ЕстьПравоВРоляхПрофиля) = Тип("Булево") Тогда
		Возврат ЕстьПравоВРоляхПрофиля;
	КонецЕсли;
	
	Отбор = Новый Структура("Профиль", СтрокаТаблицы.Профиль);
	РолиПрофиля = Контекст.РолиПрофилейГруппДоступа.НайтиСтроки(Отбор);
	
	ЕстьПравоВРоли = Ложь;
	Для Каждого ОписаниеРоли Из РолиПрофиля Цикл
		КлючПраваРоли = ОписаниеРоли.ИмяРоли + "/" + ПолноеИмя + "/" + Условие.ИмяПрава;
		ЕстьПравоВРоли = Контекст.ПраваРолейФункцииПравоДоступа.Получить(КлючПраваРоли);
		Если ЕстьПравоВРоли = Неопределено Тогда
			Если ЗначениеЗаполнено(ОписаниеРоли.ИмяРоли) Тогда
				ПолноеИмяРоли = "Роль." + ОписаниеРоли.ИмяРоли;
				Роль = Контекст.ОбъектыМетаданныхФункцииПравоДоступа.Получить(ПолноеИмяРоли);
				Если Роль = Неопределено Тогда
					Роль = Метаданные.Роли[ОписаниеРоли.ИмяРоли];
					Контекст.ОбъектыМетаданныхФункцииПравоДоступа.Вставить(ПолноеИмяРоли, Роль);
				КонецЕсли;
				ЕстьПравоВРоли = ПравоДоступа(Условие.ИмяПрава, Описание.ОбъектМетаданных, Роль,
					Описание.ИмяСтандартногоРеквизита);
			Иначе
				ЕстьПравоВРоли = Ложь;
			КонецЕсли;
			Контекст.ПраваРолейФункцииПравоДоступа.Вставить(КлючПраваРоли, ЕстьПравоВРоли);
		КонецЕсли;
		Если ЕстьПравоВРоли Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	ПраваПрофиля.Вставить(КлючПраваВРоляхПрофиля, ЕстьПравоВРоли);
	
	Возврат ЕстьПравоВРоли;
	
КонецФункции

// Для функции РассчитанноеУсловие.
Функция ЕстьРольВПрофилеГруппыДоступа(Условие, Контекст)
	
	СтрокаТаблицы = Контекст.ГруппыДоступаПрофилей.Найти(Контекст.ГруппаДоступа, "ГруппаДоступа");
	Если СтрокаТаблицы = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Отбор = Новый Структура("Профиль,ИмяРолиВерхнийРегистр", СтрокаТаблицы.Профиль, ВРег(Условие.ИмяРоли));
	
	Возврат Контекст.РолиПрофилейГруппДоступа.НайтиСтроки(Отбор).Количество() > 0;
	
КонецФункции

// Для процедуры ОбновитьПраваПорцииКлючейДоступаСписка.
Процедура ОбновитьПраваНаКлючДоступаСписка(КлючДоступа, ПраваНаКлюч, ОписаниеНовыхКлючей, ПараметрыОбновления)
	
	Если ПараметрыОбновления.РассчитыватьПраваПользователей Тогда
		ПраваНаКлючДляПользователей = Новый Соответствие;
		ТипПользователя = ПараметрыОбновления.ТипПользователя;
		Для Каждого КлючИЗначение Из ПраваНаКлюч.ДляПользователей Цикл
			Если ТипЗнч(КлючИЗначение.Ключ) = ТипПользователя Тогда
				Набор = Справочники.НаборыГруппДоступа.ПолучитьСсылку(КлючИЗначение.Ключ.УникальныйИдентификатор());
				ПраваНаКлючДляПользователей.Вставить(Набор, КлючИЗначение.Значение);
			Иначе
				ПраваНаКлюч.ДляГрупп.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Блокировка = Новый БлокировкаДанных;
	
	ЗапросГрупп = Новый Запрос;
	ЗапросГрупп.УстановитьПараметр("КлючДоступа", КлючДоступа);
	ЗапросГрупп.Текст =
	"ВЫБРАТЬ
	|	КлючиДоступаГруппДоступа.ГруппаДоступа КАК ГруппаДоступа,
	|	КлючиДоступаГруппДоступа.ПравоИзменение КАК ПравоИзменение,
	|	КлючиДоступаГруппДоступа.ПравоДобавление КАК ПравоДобавление
	|ИЗ
	|	РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
	|ГДЕ
	|	КлючиДоступаГруппДоступа.КлючДоступа = &КлючДоступа
	|	И &УточнениеПланаЗапроса";
	УстановитьУточнениеПланаЗапроса(ЗапросГрупп.Текст);
	
	ЭлементБлокировкиГрупп = Блокировка.Добавить("РегистрСведений.КлючиДоступаГруппДоступа");
	ЭлементБлокировкиГрупп.УстановитьЗначение("КлючДоступа", КлючДоступа);
	НаборЗаписейГрупп = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаГруппДоступа);
	НаборЗаписейГрупп.Отбор.КлючДоступа.Установить(КлючДоступа);
	
	ЗапросНаборовГруппДоступа = Новый Запрос;
	ЗапросНаборовГруппДоступа.УстановитьПараметр("КлючДоступа", КлючДоступа);
	ЗапросНаборовГруппДоступа.УстановитьПараметр("РазрешенныйПустойНабор",
		УправлениеДоступомСлужебныйПовтИсп.РазрешенныйПустойНаборГруппДоступа());
	
	ЗапросНаборовГруппДоступа.Текст = ТекстЗапросаВыбораРазличийПроизводныхПравДляГруппДоступа();
	ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаНаборовГруппДоступа");
	ЭлементБлокировки.УстановитьЗначение("КлючДоступа", КлючДоступа);
	НаборЗаписейНаборовГруппДоступа = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаНаборовГруппДоступа);
	НаборЗаписейНаборовГруппДоступа.Отбор.КлючДоступа.Установить(КлючДоступа);
	
	Если ПараметрыОбновления.РассчитыватьПраваПользователей Тогда
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("КлючДоступа", КлючДоступа);
		
		Если Не ПараметрыОбновления.ДляВнешнихПользователей Тогда
			Запрос.Текст = ТекстЗапросаВыбораРазличийПроизводныхПравДляПользователей();
			ИмяПоляВладельцаПрав = "Пользователь";
			ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаПользователей");
			НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаПользователей);
		Иначе
			Запрос.Текст = ТекстЗапросаВыбораРазличийПроизводныхПравДляВнешнихПользователей();
			ИмяПоляВладельцаПрав = "ВнешнийПользователь";
			ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.КлючиДоступаВнешнихПользователей");
			НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.КлючиДоступаВнешнихПользователей);
		КонецЕсли;
		ЭлементБлокировки.УстановитьЗначение("КлючДоступа", КлючДоступа);
		НаборЗаписей.Отбор.КлючДоступа.Установить(КлючДоступа);
	КонецЕсли;
	
	Если ОписаниеНовыхКлючей <> Неопределено Тогда
		ОписаниеНовогоКлюча = ОписаниеНовыхКлючей.ОписанияКлючейПоСсылке.Получить(КлючДоступа);
		КлючДоступаОбъект = ОписаниеНовогоКлюча.КлючДоступаОбъект; // СправочникОбъект.КлючиДоступа
		ЭлементБлокировкиКлюча = Блокировка.Добавить("Справочник.КлючиДоступа");
		ЭлементБлокировкиКлюча.УстановитьЗначение("Хеш",                     КлючДоступаОбъект.Хеш);
		ЭлементБлокировкиКлюча.УстановитьЗначение("Список",                  КлючДоступаОбъект.Список);
		ЭлементБлокировкиКлюча.УстановитьЗначение("ДляВнешнихПользователей", КлючДоступаОбъект.ДляВнешнихПользователей);
		ЭлементБлокировкиКлюча.УстановитьЗначение("СоставПолей",             КлючДоступаОбъект.СоставПолей);
	КонецЕсли;
	
	ОбновлениеВручную = ПараметрыОбновления.Свойство("ОбновитьПраваНаКлючи")
		И ПараметрыОбновления.ОбновитьПраваНаКлючи;
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		Если ОписаниеНовыхКлючей <> Неопределено Тогда
			ЭлементБлокировки = Блокировка.Добавить("Справочник.КлючиДоступа");
		КонецЕсли;
		ЭлементБлокировки = Блокировка.Добавить("Справочник.НаборыГруппДоступа");
		ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый;
	КонецЕсли;
	
	НачатьТранзакцию();
	Попытка
		ПередБлокировкойДанных(ПараметрыОбновления);
		Блокировка.Заблокировать();
		Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
			ЗаблокироватьРегистрыПланированияОбновленияКлючейДоступаВФайловойИБ();
		КонецЕсли;
		ПослеБлокировкиДанных(ПараметрыОбновления);
		
		ЕстьИзмененияПрав = Ложь;
		
		Если ОписаниеНовыхКлючей = Неопределено
		 Или Не НовыйКлючДоступаУжеСуществует(ОписаниеНовыхКлючей, ОписаниеНовогоКлюча, ПараметрыОбновления) Тогда
			
			Если ОписаниеНовыхКлючей <> Неопределено Тогда
				ПередЗаписьюНовогоКлюча(ПараметрыОбновления);
				КлючДоступаОбъект.Записать();
				ПослеЗаписиНовогоКлюча(ПараметрыОбновления);
			КонецЕсли;
			
			ПередЗапросомПравГруппДоступа(ПараметрыОбновления);
			РезультатЗапросаГрупп = ЗапросГрупп.Выполнить();
			ПослеЗапросаПравГруппДоступа(ПараметрыОбновления);
			
			ЕстьИзменения = Новый Структура("ПраваГруппДоступа, ПраваГруппПользователей", Ложь, Ложь);
			ОбновитьИсходныеПраваГруппНаКлючДоступа(РезультатЗапросаГрупп, НаборЗаписейГрупп, "ГруппаДоступа",
				КлючДоступа, ПраваНаКлюч.ДляГрупп, ПараметрыОбновления, ЕстьИзменения);
			
			ЕстьИзмененияПрав = ЕстьИзменения.ПраваГруппДоступа Или ЕстьИзменения.ПраваГруппПользователей;
			
			Если ЕстьИзменения.ПраваГруппДоступа Или ОбновлениеВручную Тогда
				ПередЗапросомИзмененийПроизводныхПрав(ПараметрыОбновления);
				РезультатЗапросаНаборовГруппДоступа = ЗапросНаборовГруппДоступа.Выполнить();
				ПослеЗапросаИзмененийПроизводныхПрав(ПараметрыОбновления);
				ОбновитьПроизводныеПраваНаКлючДоступа(РезультатЗапросаНаборовГруппДоступа,
					НаборЗаписейНаборовГруппДоступа, "НаборГруппДоступа", КлючДоступа, , ПараметрыОбновления);
			КонецЕсли;
			
			Если ПараметрыОбновления.РассчитыватьПраваПользователей Тогда
				ЗапросыПакета = СтрРазделить(Запрос.Текст, ";", Ложь);
				Если ЕстьИзменения.ПраваГруппПользователей Или ОбновлениеВручную Тогда
					Запрос.Текст = ЗапросыПакета[1];
					ПередЗапросомИзмененийПроизводныхПрав(ПараметрыОбновления);
					РезультатЗапроса = Запрос.Выполнить();
					ПослеЗапросаИзмененийПроизводныхПрав(ПараметрыОбновления);
					ОбновитьПроизводныеПраваНаКлючДоступа(РезультатЗапроса,
						НаборЗаписей, ИмяПоляВладельцаПрав, КлючДоступа, , ПараметрыОбновления);
					НаборЗаписей.Очистить();
				КонецЕсли;
				Запрос.Текст = ЗапросыПакета[0];
				ПередЗапросомПравПользователей(ПараметрыОбновления);
				РезультатЗапросаПользователей = Запрос.Выполнить();
				ПослеЗапросаПравПользователей(ПараметрыОбновления);
				ОбновитьИсходныеПраваПользователейНаКлючДоступа(РезультатЗапросаПользователей, НаборЗаписей,
					ИмяПоляВладельцаПрав, КлючДоступа, ПраваНаКлючДляПользователей, ЕстьИзмененияПрав, ПараметрыОбновления);
			КонецЕсли;
			
			Если ОписаниеНовыхКлючей = Неопределено
			   И ЕстьИзмененияПрав
			   И ПараметрыОбновления.ЗависимыеСпискиПоКлючамДоступа.Количество() > 0 Тогда
				
				ПередПланированиемОбновления(ПараметрыОбновления);
				ЗапланироватьОбновлениеПользователейКлючейДоступа(ПараметрыОбновления.ЗависимыеСпискиПоКлючамДоступа,
					"ОбновитьПраваНаКлючДоступаСписка",
					Не ПараметрыОбновления.ДляВнешнихПользователей,
					ПараметрыОбновления.ДляВнешнихПользователей, ,
					Новый Структура("ПоКлючамДоступа", КлючДоступа));
				ПослеПланированияОбновления(ПараметрыОбновления);
			КонецЕсли;
		КонецЕсли;
		
		// АПК:330-выкл - №783.1.3 Допустимо указать вызов после ЗафиксироватьТранзакцию,
		// так как это вызов пустой процедуры в штатном режиме (то есть исключение невозможно),
		// а в режиме анализа производительности последствия учитываются и не являются критичными.
		ПередФиксациейТранзакции(ПараметрыОбновления);
		ЗафиксироватьТранзакцию();
		ПослеФиксацииТранзакции(ПараметрыОбновления);
		// АПК:330-вкл.
	Исключение
		ОтменитьТранзакцию();
		ВызватьИсключение;
	КонецПопытки;
	
	Если ЕстьИзмененияПрав И ПараметрыОбновления.Свойство("ЕстьИзмененияПрав") Тогда
		ПараметрыОбновления.ЕстьИзмененияПрав = Истина;
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ОбновитьПраваНаКлючДоступаСписка, ОбновитьГруппыДоступаРазрешенногоКлючаДоступа.
Функция ТекстЗапросаВыбораРазличийПроизводныхПравДляГруппДоступа()
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ВсеСтроки.НаборГруппДоступа КАК НаборГруппДоступа,
	|	ВсеСтроки.ПравоИзменение КАК ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление КАК ПравоДобавление,
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки
	|ИЗ
	|	(ВЫБРАТЬ
	|		ГруппыВходящиеВНаборы.Ссылка КАК НаборГруппДоступа,
	|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоИзменение) КАК ПравоИзменение,
	|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоДобавление) КАК ПравоДобавление,
	|		1 КАК ВидИзмененияСтроки
	|	ИЗ
	|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыВходящиеВНаборы
	|			ПО (ГруппыВходящиеВНаборы.Группа = КлючиДоступаГруппДоступа.ГруппаДоступа)
	|				И (ТИПЗНАЧЕНИЯ(ГруппыВходящиеВНаборы.Группа) = ТИП(Справочник.ГруппыДоступа))
	|				И (ТИПЗНАЧЕНИЯ(КлючиДоступаГруппДоступа.ГруппаДоступа) = ТИП(Справочник.ГруппыДоступа))
	|				И (КлючиДоступаГруппДоступа.КлючДоступа = &КлючДоступа)
	|				И (&УточнениеПланаЗапроса)
	|	
	|	СГРУППИРОВАТЬ ПО
	|		ГруппыВходящиеВНаборы.Ссылка
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		&РазрешенныйПустойНабор,
	|		КлючиДоступаГруппДоступа.ПравоИзменение,
	|		КлючиДоступаГруппДоступа.ПравоДобавление,
	|		1
	|	ИЗ
	|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
	|	ГДЕ
	|		КлючиДоступаГруппДоступа.ГруппаДоступа = ЗНАЧЕНИЕ(Справочник.ГруппыДоступа.ПустаяСсылка)
	|		И КлючиДоступаГруппДоступа.КлючДоступа = &КлючДоступа
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		СтарыеДанные.НаборГруппДоступа,
	|		СтарыеДанные.ПравоИзменение,
	|		СтарыеДанные.ПравоДобавление,
	|		-1
	|	ИЗ
	|		РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК СтарыеДанные
	|	ГДЕ
	|		СтарыеДанные.КлючДоступа = &КлючДоступа) КАК ВсеСтроки
	|
	|СГРУППИРОВАТЬ ПО
	|	ВсеСтроки.НаборГруппДоступа,
	|	ВсеСтроки.ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление
	|
	|ИМЕЮЩИЕ
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
	|
	|УПОРЯДОЧИТЬ ПО
	|	ВидИзмененияСтроки";
	
	УстановитьУточнениеПланаЗапроса(ТекстЗапроса);
	
	Возврат ТекстЗапроса;
	
КонецФункции

// Для процедур ОбновитьПраваНаКлючДоступаСписка.
Функция ТекстЗапросаВыбораРазличийПроизводныхПравДляПользователей()
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	КлючиДоступаПользователей.Пользователь КАК Пользователь,
	|	КлючиДоступаПользователей.ПравоИзменение КАК ПравоИзменение,
	|	КлючиДоступаПользователей.ПравоДобавление КАК ПравоДобавление,
	|	КлючиДоступаПользователей.ЭтоПраваНабораГрупп КАК ЭтоПраваНабораГрупп
	|ИЗ
	|	РегистрСведений.КлючиДоступаПользователей КАК КлючиДоступаПользователей
	|ГДЕ
	|	КлючиДоступаПользователей.КлючДоступа = &КлючДоступа
	|	И НЕ КлючиДоступаПользователей.ЭтоПраваНабораГрупп
	|	И &УточнениеПланаЗапроса
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВсеСтроки.Пользователь КАК Пользователь,
	|	ВсеСтроки.ПравоИзменение КАК ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление КАК ПравоДобавление,
	|	ИСТИНА КАК ЭтоПраваНабораГрупп,
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки
	|ИЗ
	|	(ВЫБРАТЬ
	|		ГруппыВходящиеВНаборы.Ссылка КАК Пользователь,
	|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоИзменение) КАК ПравоИзменение,
	|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоДобавление) КАК ПравоДобавление,
	|		1 КАК ВидИзмененияСтроки
	|	ИЗ
	|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыВходящиеВНаборы
	|			ПО (ГруппыВходящиеВНаборы.Группа = КлючиДоступаГруппДоступа.ГруппаДоступа)
	|				И (ТИПЗНАЧЕНИЯ(ГруппыВходящиеВНаборы.Группа) = ТИП(Справочник.ГруппыПользователей))
	|				И (ТИПЗНАЧЕНИЯ(КлючиДоступаГруппДоступа.ГруппаДоступа) = ТИП(Справочник.ГруппыПользователей))
	|				И (КлючиДоступаГруппДоступа.КлючДоступа = &КлючДоступа)
	|				И (&УточнениеПланаЗапроса)
	|	
	|	СГРУППИРОВАТЬ ПО
	|		ГруппыВходящиеВНаборы.Ссылка
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		СтарыеДанные.Пользователь,
	|		СтарыеДанные.ПравоИзменение,
	|		СтарыеДанные.ПравоДобавление,
	|		-1
	|	ИЗ
	|		РегистрСведений.КлючиДоступаПользователей КАК СтарыеДанные
	|	ГДЕ
	|		СтарыеДанные.ЭтоПраваНабораГрупп
	|		И СтарыеДанные.КлючДоступа = &КлючДоступа) КАК ВсеСтроки
	|
	|СГРУППИРОВАТЬ ПО
	|	ВсеСтроки.Пользователь,
	|	ВсеСтроки.ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление
	|
	|ИМЕЮЩИЕ
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
	|
	|УПОРЯДОЧИТЬ ПО
	|	ВидИзмененияСтроки";
	
	УстановитьУточнениеПланаЗапроса(ТекстЗапроса);
	
	Возврат ТекстЗапроса;
	
КонецФункции

// Для процедур ОбновитьПраваНаКлючДоступаСписка.
Функция ТекстЗапросаВыбораРазличийПроизводныхПравДляВнешнихПользователей()
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	КлючиДоступаВнешнихПользователей.ВнешнийПользователь КАК ВнешнийПользователь,
	|	КлючиДоступаВнешнихПользователей.ПравоИзменение КАК ПравоИзменение,
	|	КлючиДоступаВнешнихПользователей.ПравоДобавление КАК ПравоДобавление,
	|	КлючиДоступаВнешнихПользователей.ЭтоПраваНабораГрупп КАК ЭтоПраваНабораГрупп
	|ИЗ
	|	РегистрСведений.КлючиДоступаВнешнихПользователей КАК КлючиДоступаВнешнихПользователей
	|ГДЕ
	|	КлючиДоступаВнешнихПользователей.КлючДоступа = &КлючДоступа
	|	И НЕ КлючиДоступаВнешнихПользователей.ЭтоПраваНабораГрупп
	|	И &УточнениеПланаЗапроса
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ВсеСтроки.ВнешнийПользователь КАК ВнешнийПользователь,
	|	ВсеСтроки.ПравоИзменение КАК ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление КАК ПравоДобавление,
	|	ИСТИНА КАК ЭтоПраваНабораГрупп,
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) КАК ВидИзмененияСтроки
	|ИЗ
	|	(ВЫБРАТЬ
	|		ГруппыВходящиеВНаборы.Ссылка КАК ВнешнийПользователь,
	|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоИзменение) КАК ПравоИзменение,
	|		МАКСИМУМ(КлючиДоступаГруппДоступа.ПравоДобавление) КАК ПравоДобавление,
	|		1 КАК ВидИзмененияСтроки
	|	ИЗ
	|		РегистрСведений.КлючиДоступаГруппДоступа КАК КлючиДоступаГруппДоступа
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.НаборыГруппДоступа.Группы КАК ГруппыВходящиеВНаборы
	|			ПО (ГруппыВходящиеВНаборы.Группа = КлючиДоступаГруппДоступа.ГруппаДоступа)
	|				И (ТИПЗНАЧЕНИЯ(ГруппыВходящиеВНаборы.Группа) = ТИП(Справочник.ГруппыВнешнихПользователей))
	|				И (ТИПЗНАЧЕНИЯ(КлючиДоступаГруппДоступа.ГруппаДоступа) = ТИП(Справочник.ГруппыВнешнихПользователей))
	|				И (КлючиДоступаГруппДоступа.КлючДоступа = &КлючДоступа)
	|				И (&УточнениеПланаЗапроса)
	|	
	|	СГРУППИРОВАТЬ ПО
	|		ГруппыВходящиеВНаборы.Ссылка
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		СтарыеДанные.ВнешнийПользователь,
	|		СтарыеДанные.ПравоИзменение,
	|		СтарыеДанные.ПравоДобавление,
	|		-1
	|	ИЗ
	|		РегистрСведений.КлючиДоступаВнешнихПользователей КАК СтарыеДанные
	|	ГДЕ
	|		СтарыеДанные.ЭтоПраваНабораГрупп
	|		И СтарыеДанные.КлючДоступа = &КлючДоступа) КАК ВсеСтроки
	|
	|СГРУППИРОВАТЬ ПО
	|	ВсеСтроки.ВнешнийПользователь,
	|	ВсеСтроки.ПравоИзменение,
	|	ВсеСтроки.ПравоДобавление
	|
	|ИМЕЮЩИЕ
	|	СУММА(ВсеСтроки.ВидИзмененияСтроки) <> 0
	|
	|УПОРЯДОЧИТЬ ПО
	|	ВидИзмененияСтроки";
	
	УстановитьУточнениеПланаЗапроса(ТекстЗапроса);
	
	Возврат ТекстЗапроса;
	
КонецФункции

// Для процедуры ОбновитьПраваНаКлючДоступаСписка.
Функция НовыйКлючДоступаУжеСуществует(ОписаниеНовыхКлючей, ОписаниеНовогоКлюча, ПараметрыОбновления)
	
	ЗапросСуществованияКлючей = ОписаниеНовыхКлючей.ЗапросСуществованияКлючей;
	ЗапросСуществованияКлючей.УстановитьПараметр("Хеш", ОписаниеНовогоКлюча.КлючДоступаОбъект.Хеш);
	
	Если ЗапросСуществованияКлючей.Выполнить().Пустой() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ЗапросЗначенийКлючей = ОписаниеНовыхКлючей.ЗапросЗначенийКлючей;
	ЗапросЗначенийКлючей.УстановитьПараметр("Хеши", ОписаниеНовогоКлюча.КлючДоступаОбъект.Хеш);
	
	РезультатыЗапросаЗначенийКлючей = ЗапросЗначенийКлючей.ВыполнитьПакет();
	
	ТаблицыКлюча = ПараметрыОбновления.ТаблицыКлюча;
	КлючиЗначенийСтрокКлючей = КлючиЗначенийСтрокОбъектов(РезультатыЗапросаЗначенийКлючей,
		?(Не ЗначениеЗаполнено(ПараметрыОбновления.СоставПолей)
			Или СтрНачинаетсяС(ТаблицыКлюча[0], "Шапка"), 0, 1),
		ТаблицыКлюча);
	
	Выборка = РезультатыЗапросаЗначенийКлючей[0].Выбрать();
	
	Пока Выборка.Следующий() Цикл
		Ссылка = Выборка.ТекущаяСсылка; // СправочникСсылка.КлючиДоступа
		ОписаниеКлючейЗначений = КлючиЗначенийСтрокКлючей.Получить(Ссылка);
		СтрокаДляХеша = СтрокаДляХешаКлючаДоступа(ОписаниеКлючейЗначений, ТаблицыКлюча);
		Если ОписаниеНовогоКлюча.СтрокаДляХеша = СтрокаДляХеша Тогда
			ОписаниеНовогоКлюча.КлючДоступаОбъект = Новый Структура("Ссылка", Ссылка);
			НеИспользуетсяС = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "НеИспользуетсяС");
			Если ЗначениеЗаполнено(НеИспользуетсяС) Тогда
				Объект = СлужебныйЭлемент(Неопределено, Ссылка);
				Объект.НеИспользуетсяС = '00010101';
				Объект.Записать();
			КонецЕсли;
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для процедуры ОбновитьПраваНаКлючДоступаСписка.
Процедура ОбновитьИсходныеПраваГруппНаКлючДоступа(РезультатЗапроса, НаборЗаписей, ИмяПоляВладельцаПрав,
			 КлючДоступа, ПраваНаКлюч, ПараметрыОбновления, ЕстьИзменения)
	
	Выборка = РезультатЗапроса.Выбрать();
	ТипГруппыДоступа = ПараметрыОбновления.ТипГруппыДоступа;
	ЕстьИзмененияПравГруппДоступа = Ложь;
	ЕстьИзмененияПравГруппПользователей = Ложь;
	
	КоличествоЗаписанных = 0;
	ПередЗаписьюСтрок(ПараметрыОбновления);
	Пока Выборка.Следующий() Цикл
		ВладелецПрав = Выборка[ИмяПоляВладельцаПрав];
		Права = ПраваНаКлюч.Получить(ВладелецПрав); // См. НовыеПрава
		Если Права = Неопределено Тогда
			ЭлементОтбора = НаборЗаписей.Отбор[ИмяПоляВладельцаПрав]; // ЭлементОтбора
			ЭлементОтбора.Установить(ВладелецПрав);
			НаборЗаписей.Записать();
			Если ТипЗнч(ВладелецПрав) = ТипГруппыДоступа Тогда
				ЕстьИзмененияПравГруппДоступа = Истина;
			Иначе
				ЕстьИзмененияПравГруппПользователей = Истина;
			КонецЕсли;
		ИначеЕсли Выборка.ПравоИзменение  = Права.ПравоИзменение
		        И Выборка.ПравоДобавление = Права.ПравоДобавление Тогда
			ПраваНаКлюч.Вставить(ВладелецПрав, Null);
		КонецЕсли;
	КонецЦикла;
	
	ОднаЗапись = НаборЗаписей.Добавить();
	ОднаЗапись.КлючДоступа = КлючДоступа;
	Для Каждого ОписаниеПрав Из ПраваНаКлюч Цикл
		Права = ОписаниеПрав.Значение; // См. НовыеПрава
		Если Права = Null Тогда
			Продолжить;
		КонецЕсли;
		ВладелецПрав = ОписаниеПрав.Ключ;
		ЭлементОтбора = НаборЗаписей.Отбор[ИмяПоляВладельцаПрав]; // ЭлементОтбора
		ЭлементОтбора.Установить(ВладелецПрав);
		ОднаЗапись[ИмяПоляВладельцаПрав] = ВладелецПрав;
		ОднаЗапись.ПравоИзменение  = Права.ПравоИзменение;
		ОднаЗапись.ПравоДобавление = Права.ПравоДобавление;
		НаборЗаписей.Записать();
		Если ТипЗнч(ВладелецПрав) = ТипГруппыДоступа Тогда
			ЕстьИзмененияПравГруппДоступа = Истина;
		Иначе
			ЕстьИзмененияПравГруппПользователей = Истина;
		КонецЕсли;
		КоличествоЗаписанных = КоличествоЗаписанных + 1;
	КонецЦикла;
	ПослеЗаписиСтрок(ПараметрыОбновления, КоличествоЗаписанных);
	
	ЕстьИзменения.ПраваГруппДоступа       = ЕстьИзмененияПравГруппДоступа;
	ЕстьИзменения.ПраваГруппПользователей = ЕстьИзмененияПравГруппПользователей;
	
КонецПроцедуры

// Для процедуры ОбновитьПраваНаКлючДоступаСписка.
Процедура ОбновитьИсходныеПраваПользователейНаКлючДоступа(РезультатЗапроса, НаборЗаписей,
			ИмяПоляВладельцаПрав, КлючДоступа, ПраваНаКлюч, ЕстьИзмененияПрав, ПараметрыОбновления)
	
	Выборка = РезультатЗапроса.Выбрать();
	
	КоличествоЗаписанных = 0;
	ПередЗаписьюСтрок(ПараметрыОбновления);
	Пока Выборка.Следующий() Цикл
		ВладелецПрав = Выборка[ИмяПоляВладельцаПрав];
		Права = ПраваНаКлюч.Получить(ВладелецПрав); // См. НовыеПрава
		Если Права = Неопределено Тогда
			ЭлементОтбора = НаборЗаписей.Отбор[ИмяПоляВладельцаПрав]; // ЭлементОтбора
			ЭлементОтбора.Установить(ВладелецПрав);
			НаборЗаписей.Записать();
			ЕстьИзмененияПрав = Истина;
		ИначеЕсли Выборка.ПравоИзменение  = Права.ПравоИзменение
		        И Выборка.ПравоДобавление = Права.ПравоДобавление
		        И Выборка.ЭтоПраваНабораГрупп = Ложь Тогда
			ПраваНаКлюч.Вставить(ВладелецПрав, Null);
		КонецЕсли;
	КонецЦикла;
	
	ОднаЗапись = НаборЗаписей.Добавить();
	ОднаЗапись.КлючДоступа = КлючДоступа;
	Для Каждого ОписаниеПрав Из ПраваНаКлюч Цикл
		Права = ОписаниеПрав.Значение; // См. НовыеПрава
		Если Права = Null Тогда
			Продолжить;
		КонецЕсли;
		ВладелецПрав = ОписаниеПрав.Ключ;
		ЭлементОтбора = НаборЗаписей.Отбор[ИмяПоляВладельцаПрав]; // ЭлементОтбора
		ЭлементОтбора.Установить(ВладелецПрав);
		ОднаЗапись[ИмяПоляВладельцаПрав] = ВладелецПрав;
		ОднаЗапись.ПравоИзменение  = Права.ПравоИзменение;
		ОднаЗапись.ПравоДобавление = Права.ПравоДобавление;
		ОднаЗапись.ЭтоПраваНабораГрупп = Ложь;
		НаборЗаписей.Записать();
		ЕстьИзмененияПрав = Истина;
		КоличествоЗаписанных = КоличествоЗаписанных + 1;
	КонецЦикла;
	ПослеЗаписиСтрок(ПараметрыОбновления, КоличествоЗаписанных);
	
КонецПроцедуры

// Для процедуры ОбновитьПраваНаКлючДоступаСписка.
Процедура ОбновитьПроизводныеПраваНаКлючДоступа(РезультатЗапроса, НаборЗаписей, ИмяПоляВладельцаПрав,
			КлючДоступа, ЕстьИзменения = Ложь, ПараметрыОбновления = Неопределено)
	
	Выборка = РезультатЗапроса.Выбрать();
	УдалениеЗавершено = Ложь;
	
	КоличествоЗаписанных = 0;
	ПередЗаписьюСтрок(ПараметрыОбновления);
	Пока Выборка.Следующий() Цикл
		ЭлементОтбора = НаборЗаписей.Отбор[ИмяПоляВладельцаПрав]; // ЭлементОтбора
		ЭлементОтбора.Установить(Выборка[ИмяПоляВладельцаПрав]);
		Если Не УдалениеЗавершено И Выборка.ВидИзмененияСтроки = 1 Тогда
			УдалениеЗавершено = Истина;
			ОднаЗапись = НаборЗаписей.Добавить();
			ОднаЗапись.КлючДоступа = КлючДоступа;
		КонецЕсли;
		Если УдалениеЗавершено Тогда
			ЗаполнитьЗначенияСвойств(ОднаЗапись, Выборка);
		КонецЕсли;
		НаборЗаписей.Записать();
		ЕстьИзменения = Истина;
		КоличествоЗаписанных = КоличествоЗаписанных + 1;
	КонецЦикла;
	ПослеЗаписиСтрок(ПараметрыОбновления, КоличествоЗаписанных);
	
КонецПроцедуры

// Создает служебный элемент справочника, который не участвует в подписках на события.
Функция СлужебныйЭлемент(МенеджерСправочника, Ссылка = Неопределено)
	
	Если Ссылка = Неопределено Тогда
		ЭлементСправочника = МенеджерСправочника.СоздатьЭлемент();
	Иначе
		ЭлементСправочника = Ссылка.ПолучитьОбъект();
		Если ЭлементСправочника = Неопределено Тогда
			Возврат Неопределено;
		КонецЕсли;
	КонецЕсли;
	
	ЭлементСправочника.ДополнительныеСвойства.Вставить("НеВыполнятьКонтрольУдаляемых");
	ЭлементСправочника.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
	ЭлементСправочника.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
	ЭлементСправочника.ОбменДанными.Загрузка = Истина;
	
	Возврат ЭлементСправочника;
	
КонецФункции

// Создает набор записей служебного регистра, который не участвует в подписках на события.
Функция СлужебныйНаборЗаписей(МенеджерРегистра)
	
	НаборЗаписей = МенеджерРегистра.СоздатьНаборЗаписей();
	НаборЗаписей.ДополнительныеСвойства.Вставить("НеВыполнятьКонтрольУдаляемых");
	НаборЗаписей.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
	НаборЗаписей.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
	НаборЗаписей.ОбменДанными.Загрузка = Истина;
	
	Возврат НаборЗаписей;
	
КонецФункции

// Создает менеджер значения служебной константы, которая не участвует в подписках на события.
Функция СлужебныйМенеджерЗначения(МенеджерКонстанты)
	
	МенеджерЗначения = МенеджерКонстанты.СоздатьМенеджерЗначения();
	МенеджерЗначения.ДополнительныеСвойства.Вставить("НеВыполнятьКонтрольУдаляемых");
	МенеджерЗначения.ДополнительныеСвойства.Вставить("ОтключитьМеханизмРегистрацииОбъектов");
	МенеджерЗначения.ОбменДанными.Получатели.АвтоЗаполнение = Ложь;
	МенеджерЗначения.ОбменДанными.Загрузка = Истина;
	
	Возврат МенеджерЗначения;
	
КонецФункции

// Для процедуры ЗарегистрироватьПланированиеОбновленияДоступа.
Процедура ЗарегистрироватьПланированиеОбновленияДоступаВЖурнале(Списки, ПараметрыПланирования)
	
	КомментарийДляЖурнала = НСтр("ru = 'Источник'", ОбщегоНазначения.КодОсновногоЯзыка())
		+ ": " + ПараметрыПланирования.Описание + Символы.ПС;
	
	Если Списки.Количество() > 1 Тогда
		КомментарийДляЖурнала = КомментарийДляЖурнала
			+ НСтр("ru = 'Списки'", ОбщегоНазначения.КодОсновногоЯзыка()) + ":"
			+ Символы.ПС + Символы.Таб + СтрСоединить(Списки, Символы.ПС + Символы.Таб);
	Иначе
		КомментарийДляЖурнала = КомментарийДляЖурнала
			+ НСтр("ru = 'Список'", ОбщегоНазначения.КодОсновногоЯзыка());
		
		КомментарийДляЖурнала = КомментарийДляЖурнала + " = " + Списки[0];
	КонецЕсли;
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + "ЭтоТочечноеЗадание"
		+ " = " + ?(ПараметрыПланирования.Свойство("ЭтоТочечноеЗадание"), "Да", "Нет");
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + "КлючиДоступаКДанным"
		+ " = " + ?(ПараметрыПланирования.КлючиДоступаКДанным, "Да", "Нет");
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + "РазрешенныеКлючиДоступа"
		+ " = " + ?(ПараметрыПланирования.РазрешенныеКлючиДоступа, "Да", "Нет");
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + "ДляПользователей"
		+ " = " + ?(ПараметрыПланирования.ДляПользователей, "Да", "Нет");
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + "ДляВнешнихПользователей"
		+ " = " + ?(ПараметрыПланирования.ДляВнешнихПользователей, "Да", "Нет");
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + "ЭтоПродолжениеОбновления"
		+ " = " + ?(ПараметрыПланирования.ЭтоПродолжениеОбновления, "Да", "Нет");
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + "ЭтоОбработкаУстаревшихЭлементов"
	 + " = " + ?(ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов, "Да", "Нет");
	
	ВедущийОбъект = Неопределено;
	ОписаниеВедущегоОбъекта = ПараметрыПланирования.ВедущийОбъект; // См. ОписаниеВедущегоОбъекта
	
	Если ТипЗнч(ОписаниеВедущегоОбъекта) = Тип("Структура") Тогда
		Если ОписаниеВедущегоОбъекта.Свойство("ПоЗначениямПолей") Тогда
			Указатели = ОписаниеВедущегоОбъекта.ПоЗначениямПолей.Описание;
			ВедущийОбъект = "ПоЗначениямПолей";
		ИначеЕсли ОписаниеВедущегоОбъекта.Свойство("ПоКлючамДоступа") Тогда
			Указатели = ОписаниеВедущегоОбъекта.ПоКлючамДоступа;
			ВедущийОбъект = "ПоКлючамДоступа";
		ИначеЕсли ОписаниеВедущегоОбъекта.Свойство("ПоЗначениямСГруппами") Тогда
			Указатели = ОписаниеВедущегоОбъекта.ПоЗначениямСГруппами;
			ВедущийОбъект = "ПоЗначениямСГруппами";
		КонецЕсли;
		Если ВедущийОбъект <> Неопределено Тогда
			Если ТипЗнч(Указатели) = Тип("Массив") И Указатели.Количество() > 1 Тогда
				ВедущийОбъект = ВедущийОбъект + ":";
				Для Каждого Указатель Из Указатели Цикл
					ВедущийОбъект = ВедущийОбъект + Символы.ПС + """" + Строка(Указатель) + """ "
						+ ОписаниеУказателяВедущегоОбъекта(Указатель, ПараметрыПланирования);
				КонецЦикла;
			Иначе
				Указатель = ?(ТипЗнч(Указатели) = Тип("Массив"), Указатели[0], Указатели);
				ВедущийОбъект = ВедущийОбъект + ": """ + Строка(Указатель) + """ "
					+ ОписаниеУказателяВедущегоОбъекта(Указатель, ПараметрыПланирования);
			КонецЕсли;
		КонецЕсли;
		Если ОписаниеВедущегоОбъекта.Свойство("ПоДаннымКэшаРасчетаПрав") Тогда
			ВедущийОбъект = "ПоДаннымКэшаРасчетаПрав" + ": """
				+ ОписаниеВедущегоОбъекта.ПоДаннымКэшаРасчетаПрав;
		КонецЕсли;
	КонецЕсли;
	
	Попытка
		ВызватьИсключение НСтр("ru = 'Стек вызовов'");
	Исключение
		СтекВызовов = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
	КонецПопытки;
	
	КомментарийДляЖурнала = КомментарийДляЖурнала + Символы.ПС + Символы.ПС + СтекВызовов;
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Управление доступом.Показатели.Планирование обновления доступа'",
		     ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Информация, ,
		ВедущийОбъект,
		КомментарийДляЖурнала,
		?(ТранзакцияАктивна(), РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная, Неопределено));
	
КонецПроцедуры

// Для процедуры ЗарегистрироватьПланированиеОбновленияДоступаВЖурнале.
Функция ОписаниеУказателяВедущегоОбъекта(Указатель, ПараметрыПланирования)
	
	Если ПараметрыПланирования.Свойство("ТиповСсылокВедущихОбъектов") Тогда
		ТипыСсылок = ПараметрыПланирования.ТиповСсылокВедущихОбъектов;
	Иначе
		ТипыСсылок = УправлениеДоступомСлужебныйПовтИсп.ТиповСсылокВедущихОбъектов();
		ПараметрыПланирования.Вставить("ТиповСсылокВедущихОбъектов", ТипыСсылок);
	КонецЕсли;
	
	Если ТипыСсылок.Получить(ТипЗнч(Указатель)) <> Неопределено Тогда
		Возврат ПолучитьНавигационнуюСсылку(Указатель);
	КонецЕсли;
	
	Описание = "";
	Для Каждого ЭлементОтбора Из Указатель Цикл
		Если Не ЭлементОтбора.Использование Тогда
			Продолжить;
		КонецЕсли;
		Если ТипыСсылок.Получить(ТипЗнч(ЭлементОтбора.Значение)) <> Неопределено Тогда
			ОписаниеЗначения = ПолучитьНавигационнуюСсылку(ЭлементОтбора.Значение);
		ИначеЕсли ТипЗнч(ЭлементОтбора.Значение) = Тип("Неопределено") Тогда
			ОписаниеЗначения = НСтр("ru = 'Неопределено'");
		Иначе
			ОписаниеЗначения = Формат(ЭлементОтбора.Значение, "ЧН=0; ДП='01.01.0001 00:00:00'");
		КонецЕсли;
		Описание = Описание + ?(Описание = "", "", ", ")
			+ ЭлементОтбора.Имя + " = " + ОписаниеЗначения;
	КонецЦикла;
	
	Возврат Описание;
	
КонецФункции

// Для вызова из мест планирования обновления доступа.
Процедура ЗарегистрироватьПланированиеОбновленияДоступа(СпискиПоИдентификаторам, ПараметрыПланирования, ВсеСписки = Ложь)
	
	Если Не РегистрироватьПоказателиПланированияОбновленияДоступа() Тогда
		Возврат;
	КонецЕсли;
	
	Если ПараметрыПланирования = Неопределено Тогда
		ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа();
	КонецЕсли;
	
	Списки = Новый Массив;
	
	Если Не ВсеСписки Тогда
		Для Каждого ОписаниеСписка Из СпискиПоИдентификаторам Цикл
			Если ЗначениеЗаполнено(ОписаниеСписка.Значение) Тогда
				Списки.Добавить(ОписаниеСписка.Значение);
			Иначе
				Списки.Добавить(ПолноеИмяСписка(ОписаниеСписка.Ключ));
			КонецЕсли;
		КонецЦикла;
	Иначе
		Списки.Добавить("Все");
	КонецЕсли;
	
	ЗарегистрироватьПланированиеОбновленияДоступаВЖурнале(Списки, ПараметрыПланирования);
	
КонецПроцедуры

// Для процедуры ЗарегистрироватьПланированиеОбновленияДоступа.
Функция ПолноеИмяСписка(ОписаниеСписка)
	
	Если ТипЗнч(ОписаниеСписка) = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных")
	 Или ТипЗнч(ОписаниеСписка) = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений") Тогда
		
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоИдентификатору(ОписаниеСписка, Ложь);
		
		Если ТипЗнч(ОбъектМетаданных) = Тип("ОбъектМетаданных") Тогда
			Возврат ОбъектМетаданных.ПолноеИмя();
		Иначе
			Возврат ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ОписаниеСписка, "ПолноеИмя");
		КонецЕсли;
	Иначе
		Возврат Строка(ОписаниеСписка);
	КонецЕсли;
	
КонецФункции

// Для процедуры УправлениеДоступом.ОтключитьОбновлениеКлючейДоступа.
//
// Параметры:
//   Списки - Массив
//   ДобавленныеСписки - Соответствие
//   НедоступныеСписки - Массив - возвращаемое значение.
//
Процедура ДобавитьЗависимыеСписки(Списки, ДобавленныеСписки, НедоступныеСписки) Экспорт
	
	ИсходныеСписки = Новый ФиксированныйМассив(Списки);
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь);
	
	НенайденныеСписки = Новый Массив;
	
	Для Каждого ИсходныйСписок Из ИсходныеСписки Цикл
		Свойства = ДействующиеПараметры.ВедущиеСписки.Получить(ИсходныйСписок);
		Если Свойства = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Для Каждого КлючИЗначение Из Свойства.ЗависимыеСписки Цикл
			Если ДобавленныеСписки.Получить(КлючИЗначение.Ключ) = Неопределено Тогда
				ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(КлючИЗначение.Ключ);
				Если ОбъектМетаданных = Неопределено Тогда
					НенайденныеСписки.Добавить(КлючИЗначение.Ключ);
				Иначе
					ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
					Списки.Добавить(ПолноеИмя);
					ДобавленныеСписки.Вставить(ПолноеИмя, Истина);
				КонецЕсли;
				ДобавленныеСписки.Вставить(КлючИЗначение.Ключ, Истина);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Если НенайденныеСписки.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	НедоступныеСписки = ВсеИдентификаторыСПодобнымиПолнымиИменами(НенайденныеСписки);
	
КонецПроцедуры

Функция ОписаниеОграниченийДанных() Экспорт
	
	ОбщийКонтекст = Новый Структура;
	ОбщийКонтекст.Вставить("СпискиСОграничением", УправлениеДоступомСлужебныйПовтИсп.СпискиСОграничением());
	
	Результат = Новый Соответствие;
	
	Для Каждого ОписаниеСписка Из ОбщийКонтекст.СпискиСОграничением Цикл
		ПолноеИмя = ОписаниеСписка.Ключ;
		ОписаниеОграничения = ОписаниеОграниченияДанных(ОбщийКонтекст, ПолноеИмя);
		Результат.Вставить(ПолноеИмя, ОписаниеОграничения);
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

#Область ПараметрыДляПереопределенияЧерезРасширениеКонфигурации

Функция КоличествоЧасовУстареванияНеиспользуемыхЭлементов()
	
	Возврат 47;
	
КонецФункции

Функция КоличествоЧасовМеждуПланированиемОбработкиУстаревшихЭлементов()
	
	Возврат 48;
	
КонецФункции

Функция МаксимальноеКоличествоМинутВыполненияФоновогоЗаданияОбновленияДоступа()
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
		Возврат 2;
	Иначе
		Возврат 15;
	КонецЕсли;
	
КонецФункции

Функция МаксимальноеКоличествоСекундОжиданияВыполненияОдногоЗаданияВПотоке()
	
	Возврат 900; // 15 минут (например, избыточно длительный запрос SQL).
	
КонецФункции

Функция МинимальноеКоличествоСекундОбработкиПорцииВОтдельномПотоке()
	
	Возврат 1;
	
КонецФункции

Функция МинимальноеКоличествоСекундВыполненияТочечногоЗадания()
	
	Возврат 15;
	
КонецФункции

Функция ЗагружатьСвободныеПотокиСледующимиЗаданиямиПриДлительныхЗапросах()
	
	Возврат Истина;
	
КонецФункции

Функция МаксимальныйПериодПолученияПорцийЗапросом()
	
	Возврат "Год"; // Год, Квартал, Месяц, Неделя.
	
КонецФункции

Функция ЗапускатьОбновлениеПолученныхПорцийПриПолученииНовыхПорций()
	
	Возврат Ложь;
	
КонецФункции

Функция МаксимальноеКоличествоСекундБыстрогоПолученияПорцийЭлементовДанных()
	
	Возврат 6; // 0 - отключить балансировку нагрузки на диск.
	
КонецФункции

Функция КоличествоЭлементовДанныхВЗапросе()
	
	Возврат 1000;
	
КонецФункции

Функция КоличествоЭлементовДанныхВПорции()
	
	Возврат 1000;
	
КонецФункции

Функция КоличествоКлючейДоступаВЗапросе()
	
	Возврат 1000;
	
КонецФункции

Функция КоличествоКлючейДоступаВПорции()
	
	Возврат 200;
	
КонецФункции

Функция ЗаписыватьТолькоИзмененныеКлючиДоступаЭлементовДанных()
	
	Возврат Ложь;
	
КонецФункции

Функция МаксимальноеКоличествоКомбинацийЗначенийВедущихПолейПриВычисленииСоставаИзмененных()
	
	Возврат 100;
	
КонецФункции

Функция КоличествоСекундПередОтключениемРегламентногоЗаданияПослеПолногоЗавершенияОбновления()
	
	Возврат 15;
	
КонецФункции

Функция РегистрироватьПоказателиОбновленияДоступа()
	
	Возврат Истина;
	
КонецФункции

Функция РегистрироватьПоказателиПланированияОбновленияДоступа()
	
	Возврат Ложь;
	
КонецФункции

Функция РегистрироватьСтрокуВерсииПараметровОграниченияДоступа()
	
	Возврат Ложь;
	
КонецФункции

#КонецОбласти

#Область ТочкиПодключенияДляАнализаПроизводительностиЧерезРасширениеКонфигурации

Процедура ПередБлокировкойДанных(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеБлокировкиДанных(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередЗапросомТекущихКлючейДоступа(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеЗапросаТекущихКлючейДоступа(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередЗаписьюСтрок(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеЗаписиСтрок(ПараметрыОбновления, КоличествоЗаписанных)
	Возврат;
КонецПроцедуры

Процедура ПередФиксациейТранзакции(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеФиксацииТранзакции(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередПланированиемОбновления(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеПланированияОбновления(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередЗаписьюНовогоКлюча(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеЗаписиНовогоКлюча(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередЗапросомПравГруппДоступа(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеЗапросаПравГруппДоступа(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередЗапросомИзмененийПроизводныхПрав(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеЗапросаИзмененийПроизводныхПрав(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередЗапросомПравПользователей(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПослеЗапросаПравПользователей(ПараметрыОбновления)
	Возврат;
КонецПроцедуры

Процедура ПередИзменениемПараметровСеансаДляШаблонов(НовыеЗначения, ЭтоУстановка)
	Возврат;
КонецПроцедуры

Процедура ПриОшибкеПроверкиАктуальностиМетаданных(ТекстОшибки)
	Возврат;
КонецПроцедуры

#КонецОбласти

#КонецОбласти

#Область ПараметрыОграниченияДоступа

#Область ПараметрыОграниченияДоступаОбщаяЧасть

// Основная функция, возвращающая параметры, необходимые для регистрации
// необходимости обновления ключей доступа к элементам данных.
//
// Параметры:
//  ПолноеИмя               - Строка - полное имя списка
//  ИдентификаторТранзакции - УникальныйИдентификатор
//  ПовторныйВызов          - Булево - только при вызове из самой же функции
//
// Возвращаемое значение:
//  Структура:
//   * ЗависимыеСписки      - Соответствие из КлючИЗначение:
//      ** Ключ     - Строка - полное имя списка.
//      ** Значение - Булево - Истина.
//   * ПоЗначениямПолей     - см. ВедущийСписокПоЗначениямПолей
//   * ПоКлючамДоступа      - см. ВедущийСписокПоКлючамДоступаИлиЗначениямСГруппами
//   * ПоЗначениямСГруппами - см. ВедущийСписокПоКлючамДоступаИлиЗначениямСГруппами
//
Функция СвойстваСпискаКакВедущего(ПолноеИмя, ИдентификаторТранзакции = Неопределено, ПовторныйВызов = Ложь) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(ИдентификаторТранзакции, Неопределено, ПовторныйВызов);
	ХранимыеСвойстваСпискаКакВедущего = ДействующиеПараметры.ВедущиеСписки.Получить(ПолноеИмя);
	
	Если ХранимыеСвойстваСпискаКакВедущего = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Кэш = КэшПараметровОграничения();
	
	СвойстваСпискаКакВедущего = Кэш.ВедущиеСпискиПроверенные.Получить(ПолноеИмя);
	Если СвойстваСпискаКакВедущего <> Неопределено Тогда
		Возврат СвойстваСпискаКакВедущего;
	КонецЕсли;
	
	СвойстваСпискаКакВедущего = Новый Структура(ХранимыеСвойстваСпискаКакВедущего);
	Отказ = Ложь;
	ЗаполнитьТекстЗапросаСтарыхЗначенийДляПроверкиИзмененияПолейВедущегоСписка(ПолноеИмя,
		СвойстваСпискаКакВедущего, Отказ);
	
	Если Не Отказ Тогда
		Кэш.ВедущиеСпискиПроверенные.Вставить(ПолноеИмя,
			Новый ФиксированнаяСтруктура(СвойстваСпискаКакВедущего));
		
		Возврат СвойстваСпискаКакВедущего;
	КонецЕсли;
	
	Если ПовторныйВызов Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось подготовить свойства списка ""%1"" как ведущего,
			           |из-за некорректного состояния параметров ограничения доступа.'"),
			ПолноеИмя);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Возврат СвойстваСпискаКакВедущего(ПолноеИмя, ИдентификаторТранзакции, Истина);
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * ВедущиеСпискиПроверенные - Соответствие из КлючИЗначение:
//       ** Ключ     - Строка - полное имя списка
//       ** Значение - см. УправлениеДоступомСлужебный.СвойстваСпискаКакВедущего
//   * ОграниченияСписков - Соответствие из КлючИЗначение:
//       ** Ключ     - Строка - полное имя списка
//       ** Значение - см. УправлениеДоступомСлужебный.РассчитанныеПараметрыОграничения
//   * ИдентификаторыТранзакции - Соответствие из КлючИЗначение:
//       ** Ключ     - УникальныйИдентификатор - произвольный УИД.
//       ** Значение - Булево - значение Истина.
//   * ВидыОграниченийПравДляПользователей        - Неопределено
//                                                - Строка
//   * ВидыОграниченийПравДляВнешнихПользователей - Неопределено
//                                                - Строка
//
Функция НовыйКэшПараметровОграничения() Экспорт
	
	Хранилище = Новый Структура;
	Хранилище.Вставить("ВедущиеСпискиПроверенные", Новый Соответствие);
	Хранилище.Вставить("ОграниченияСписков",       Новый Соответствие);
	Хранилище.Вставить("ИдентификаторыТранзакции", Новый Соответствие);
	Хранилище.Вставить("ВидыОграниченийПравДляПользователей",        Неопределено);
	Хранилище.Вставить("ВидыОграниченийПравДляВнешнихПользователей", Неопределено);
	
	Возврат Хранилище;
	
КонецФункции

// Для функции СвойстваСпискаКакВедущего и процедур ЗаполнитьПараметрыОграничения,
// ОбновитьИдентификаторыТранзакции, УстановитьВерсиюПараметров.
//
Функция КэшПараметровОграничения()
	
	КлючДанныхПовторногоИспользования = Строка(ПараметрыСеанса.КлючДанныхПовторногоИспользования);
	
	Возврат УправлениеДоступомСлужебныйПовтИсп.КэшПараметровОграничения(КлючДанныхПовторногоИспользования);
	
КонецФункции

Процедура СброситьКэшПараметровОграничения()
	
	Кэш = КэшПараметровОграничения();
	НовыйКэш = НовыйКэшПараметровОграничения();
	
	Для Каждого КлючИЗначение Из НовыйКэш Цикл
		Кэш.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	
КонецПроцедуры

// Для функции СвойстваСпискаКакВедущего.
Процедура ЗаполнитьТекстЗапросаСтарыхЗначенийДляПроверкиИзмененияПолейВедущегоСписка(ПолноеИмя, Свойства, Отказ)
	
	Свойства.Удалить("ЗависимыеСписки");
	Если Свойства.ПоЗначениямПолей = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ПоЗначениямПолей = Новый Структура(Свойства.ПоЗначениямПолей); // См. ВедущийСписокПоЗначениямПолей
	ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя);
	
	Если ПоЗначениямПолей.ЭтоСсылочныйТип Тогда
		Если ЗначениеЗаполнено(ПоЗначениямПолей.ПоляШапки.ВсеПоля) Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	ТекущаяТаблица.Поле1 КАК Поле1
			|ИЗ
			|	(ВЫБРАТЬ
			|		ИСТИНА КАК ЗначениеИстина) КАК ЗначениеИстина
			|		ЛЕВОЕ СОЕДИНЕНИЕ ТекущаяТаблицаЭлементовДанных КАК ТекущаяТаблица
			|		ПО (ТекущаяТаблица.Ссылка = &СсылкаНаОбъект)";
			ЗаполнитьПоляВыбораТекущейТаблицы(ТекстЗапроса,
				ПолноеИмя, ПоЗначениямПолей.ПоляШапки.ВсеПоля, ОбъектМетаданных, Отказ);
		Иначе
			ТекстЗапроса = "";
		КонецЕсли;
		
		Для Каждого ТабличнаяЧасть Из ПоЗначениямПолей.ТабличныеЧасти Цикл
			Коллекции = Новый Структура("СтандартныеТабличныеЧасти, ТабличныеЧасти");
			ТабличнаяЧастьИмя = ТабличнаяЧасть.Имя;
			ЗаполнитьЗначенияСвойств(Коллекции, ОбъектМетаданных);
			МетаданныеТаблицы = Неопределено;
			Если ТипЗнч(Коллекции.ТабличныеЧасти) = Тип("КоллекцияОбъектовМетаданных") Тогда
				МетаданныеТаблицы = ОбъектМетаданных.ТабличныеЧасти.Найти(ТабличнаяЧастьИмя);
			КонецЕсли;
			Если МетаданныеТаблицы = Неопределено
			   И ТипЗнч(Коллекции.СтандартныеТабличныеЧасти) = Тип("ОписанияСтандартныхТабличныхЧастей") Тогда
				Для Каждого СтандартнаяТабличнаяЧасть Из Коллекции.СтандартныеТабличныеЧасти Цикл
					СтандартнаяТабличнаяЧасть = СтандартнаяТабличнаяЧасть; // ОписаниеСтандартнойТабличнойЧасти
					Если СтандартнаяТабличнаяЧасть.Имя = ТабличнаяЧастьИмя Тогда
						МетаданныеТаблицы = СтандартнаяТабличнаяЧасть;
						Прервать;
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			Если МетаданныеТаблицы = Неопределено Тогда
				Отказ = Истина;
				Прервать;
			КонецЕсли;
			
			ТекстЗапросаТабличнойЧасти =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 100
			|	ТекущаяТаблица.Поле1 КАК Поле1
			|ИЗ
			|	ТекущаяТаблицаЭлементовДанных КАК ТекущаяТаблица
			|ГДЕ
			|	ТекущаяТаблица.Ссылка = &СсылкаНаОбъект";
			ЗаполнитьПоляВыбораТекущейТаблицы(ТекстЗапросаТабличнойЧасти,
				ПолноеИмя + "." + ТабличнаяЧастьИмя, ТабличнаяЧасть.ВсеПоля, МетаданныеТаблицы, Отказ, Истина);
			
			ТекстЗапроса = ТекстЗапроса + ?(ТекстЗапроса = "", "",
				ОбщегоНазначения.РазделительПакетаЗапросов()) + ТекстЗапросаТабличнойЧасти;
		КонецЦикла;
		
	ИначеЕсли ЗначениеЗаполнено(ПоЗначениямПолей.ПоляШапки.ВсеПоля) Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 100
		|	ТекущаяТаблица.Поле1 КАК Поле1
		|ИЗ
		|	ТекущаяТаблицаЭлементовДанных КАК ТекущаяТаблица
		|ГДЕ
		|	&ОтборПоИзмерениям";
		ЗаполнитьПоляВыбораТекущейТаблицы(ТекстЗапроса,
			ПолноеИмя, ПоЗначениямПолей.ПоляШапки.ВсеПоля, ОбъектМетаданных, Отказ);
	Иначе
		ТекстЗапроса = "";
	КонецЕсли;
	
	ПоЗначениямПолей.Вставить("ТекстЗапроса", ТекстЗапроса);
	Свойства.ПоЗначениямПолей = Новый ФиксированнаяСтруктура(ПоЗначениямПолей);
	
КонецПроцедуры

// Для процедуры ЗаполнитьТекстЗапросаСтарыхЗначенийДляПроверкиИзмененияПолейВедущегоСписка.
Процедура ЗаполнитьПоляВыбораТекущейТаблицы(ТекстЗапроса, ПолноеИмя, ОписаниеПолей,
			МетаданныеТаблицы, Отказ, ЭтоТабличнаяЧасть = Ложь)
	
	КоллекцииПолей = Новый Структура("Реквизиты, Измерения, Ресурсы, СтандартныеРеквизиты");
	ЗаполнитьЗначенияСвойств(КоллекцииПолей, МетаданныеТаблицы);
	
	ПоляВыбора = "";
	Для Каждого ОписаниеПоля Из ОписаниеПолей Цикл
		ИмяПоля = ?(ТипЗнч(ОписаниеПолей) = Тип("ФиксированныйМассив"), ОписаниеПоля, ОписаниеПоля.Ключ);
		
		Если Не ПолеСуществует(КоллекцииПолей.Реквизиты, ИмяПоля)
		   И Не ПолеСуществует(КоллекцииПолей.Измерения, ИмяПоля)
		   И Не ПолеСуществует(КоллекцииПолей.Ресурсы, ИмяПоля)
		   И Не ПолеСуществует(КоллекцииПолей.СтандартныеРеквизиты, ИмяПоля)
		   И Не (ЭтоТабличнаяЧасть
		         И (ВРег(ИмяПоля) = ВРег("Ссылка") // @Non-NLS
		            Или ВРег(ИмяПоля) = ВРег("Ref"))) Тогда
			
			Отказ = Истина;
			Прервать;
		КонецЕсли;
		
		ПоляВыбора = ПоляВыбора + ?(ПоляВыбора = "", "", "," + Символы.ПС)
			+ "ТекущаяТаблица." + ИмяПоля + " КАК " + ИмяПоля; // @query-part-2
	КонецЦикла;
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
		"ТекущаяТаблица.Поле1 КАК Поле1", ТекстСОтступом(СокрЛ(ПоляВыбора), "	")); // @query-part-1
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТекущаяТаблицаЭлементовДанных", ПолноеИмя);
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПЕРВЫЕ 100", "ПЕРВЫЕ " + Формат( // @query-part-1 @query-part-2
		МаксимальноеКоличествоКомбинацийЗначенийВедущихПолейПриВычисленииСоставаИзмененных() * 10, "ЧГ="));
	
КонецПроцедуры

// Для процедуры ЗаполнитьПоляВыбораТекущейТаблицы.
Функция ПолеСуществует(Коллекция, ИмяПоля)
	
	Если ТипЗнч(Коллекция) = Тип("КоллекцияОбъектовМетаданных") Тогда
		Возврат Коллекция.Найти(ИмяПоля) <> Неопределено;
		
	ИначеЕсли ТипЗнч(Коллекция) = Тип("ОписанияСтандартныхРеквизитов") Тогда
		Для Каждого СтандартныйРеквизит Из Коллекция Цикл
			Если СтандартныйРеквизит.Имя = ИмяПоля Тогда
				Возврат Истина;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Возврат Ложь;
	
КонецФункции

// Основная функция, возвращающая параметры, необходимые для проверки прав в момент записи элементов данных.
//
// Возвращаемое значение:
//   см. ПараметрыОграниченияПоСтруктуреОграничения
//
Функция ПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции = Неопределено, ДляВнешнихПользователей = Неопределено) Экспорт
	
	УстановитьОтключениеБезопасногоРежима(Истина);
	УстановитьПривилегированныйРежим(Истина);
	
	ДействующиеПараметрыОграниченияДоступа(ИдентификаторТранзакции, Неопределено, Ложь);
	Параметры = КэшПараметровОграничения().ОграниченияСписков.Получить(ПолноеИмя);
	
	Если Параметры = Неопределено Тогда
		ЗаполнитьПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции, Параметры);
	КонецЕсли;
	
	Если ДляВнешнихПользователей = Null Тогда
		Возврат Параметры;
	КонецЕсли;
	
	Если ДляВнешнихПользователей = Неопределено Тогда
		ДляВнешнихПользователей = Пользователи.ЭтоСеансВнешнегоПользователя();
	КонецЕсли;
	
	Если ДляВнешнихПользователей Тогда
		Возврат Параметры.ДляВнешнихПользователей;
	КонецЕсли;
	
	Возврат Параметры.ДляПользователей;
	
КонецФункции

// Для функций СвойстваСпискаКакВедущего, ПараметрыОграничения.
Процедура ЗаполнитьПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции, Параметры,
			ОбщийКонтекст = Неопределено, ПовторныйВызов = Ложь)
	
	Если ОбщийКонтекст = Неопределено Тогда
		ОбщийКонтекст = ОбщийКонтекстРасчетаПараметровОграничения(ПолноеИмя);
		ОбщийКонтекст.Вставить("ОписанияОграничений", Новый Соответствие);
	КонецЕсли;
	
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(ИдентификаторТранзакции, ОбщийКонтекст, Ложь);
	
	ВерсияОграничений = ДействующиеПараметры.ВерсииОграниченийСписков.Получить(ПолноеИмя);
	РассчитанныеПараметры = РассчитанныеПараметрыОграничения(ПолноеИмя, ОбщийКонтекст, ДействующиеПараметры);
	
	Если ВерсияОграничений <> РассчитанныеПараметры.Версия Тогда
		Если ПовторныйВызов Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось обновить параметры ограничения доступа списка ""%1""
				           |из-за нестабильной строки свойств версии параметров для вычисления хеш-суммы.'"),
				ПолноеИмя);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(ИдентификаторТранзакции, ОбщийКонтекст, Истина);
		ЗаполнитьПараметрыОграничения(ПолноеИмя, ИдентификаторТранзакции, Параметры, ОбщийКонтекст, Истина);
		Возврат;
	КонецЕсли;
	
	Параметры = ОбщегоНазначения.ФиксированныеДанные(РассчитанныеПараметры);
	
	КэшПараметровОграничения().ОграниченияСписков.Вставить(ПолноеИмя, Параметры);
	
КонецПроцедуры

// Для процедуры ЗаполнитьПараметрыОграничения и функций
// ХранимыеПараметрыОграниченияДоступа, ОшибкиОграниченийДоступа.
//
// Возвращаемое значение:
//  Структура:
//    * СвойстваВидовДоступа         - см. СвойстваВидовДоступа
//    * ТипыПользователя             - Массив из Тип
//    * ТипыВладельцевНастроекПрав   - ФиксированноеСоответствие
//    * ОтдельныеТаблицыНастроекПрав - ФиксированноеСоответствие
//    * ВнешниеПользователиВключены  - Булево
//    * ИспользуемыеТипыЗначений     - см. ИспользуемыеТипыЗначений
//    * СпискиСОграничением          - см. УправлениеДоступомСлужебныйПовтИсп.СпискиСОграничением
//
Функция ОбщийКонтекстРасчетаПараметровОграничения(ПолноеИмя = Неопределено,
			ВсеВидыДоступаИспользуются = Неопределено, ЗаполнитьСпискиСОграничением = Истина)
	
	ТипыПользователя = Новый Массив;
	ТипыПользователя.Добавить(Тип("СправочникСсылка.Пользователи"));
	ТипыПользователя.Добавить(Тип("СправочникСсылка.ГруппыПользователей"));
	ТипыПользователя.Добавить(Тип("СправочникСсылка.ВнешниеПользователи"));
	ТипыПользователя.Добавить(Тип("СправочникСсылка.ГруппыВнешнихПользователей"));
	
	ОграничениеДоступаВключено = Константы.ОграничиватьДоступНаУровнеЗаписей.Получить();
	Если УправлениеДоступом.ОграничиватьДоступНаУровнеЗаписей() <> ОграничениеДоступаВключено Тогда
		ОбновитьПовторноИспользуемыеЗначения();
	КонецЕсли;
	СвойстваВидовДоступа = СвойстваВидовДоступа();
	
	ИспользуемыеТипыЗначений = ИспользуемыеТипыЗначений(СвойстваВидовДоступа, ПолноеИмя, ВсеВидыДоступаИспользуются);
	Если ТипЗнч(ВсеВидыДоступаИспользуются) = Тип("Булево") Тогда
		ОграничениеДоступаВключено  = ВсеВидыДоступаИспользуются;
		ВнешниеПользователиВключены = ВсеВидыДоступаИспользуются;
	Иначе
		ВнешниеПользователиВключены = Константы.ИспользоватьВнешнихПользователей.Получить();
	КонецЕсли;
	
	ВозможныеПрава = ВозможныеПраваДляНастройкиПравОбъектов();
	
	ОбщийКонтекст = Новый Структура;
	ОбщийКонтекст.Вставить("СвойстваВидовДоступа",         СвойстваВидовДоступа);
	ОбщийКонтекст.Вставить("ТипыПользователя",             ТипыПользователя);
	ОбщийКонтекст.Вставить("ТипыВладельцевНастроекПрав",   ВозможныеПрава.ПоТипамСсылок);
	ОбщийКонтекст.Вставить("ОтдельныеТаблицыНастроекПрав", ВозможныеПрава.ОтдельныеТаблицы);
	ОбщийКонтекст.Вставить("ВнешниеПользователиВключены",  ВнешниеПользователиВключены);
	ОбщийКонтекст.Вставить("ОграничениеДоступаВключено",   ОграничениеДоступаВключено);
	ОбщийКонтекст.Вставить("ИспользуемыеТипыЗначений",     ИспользуемыеТипыЗначений);
	
	Если ЗаполнитьСпискиСОграничением Тогда
		ОбщийКонтекст.Вставить("СпискиСОграничением", УправлениеДоступомСлужебныйПовтИсп.СпискиСОграничением());
	КонецЕсли;
	
	Возврат ОбщийКонтекст;
	
КонецФункции

// Для функций ОбщийКонтекстРасчетаПараметровОграничения и ИспользованиеВидовДоступаИзменено.
//
// Параметры:
//  СвойстваВидовДоступа - см. СвойстваВидовДоступа
//  ПолноеИмя - Строка
//  ВсеВидыДоступаИспользуются - Неопределено
//                             - Булево
//  ТолькоХешСумма             - Булево
// 
// Возвращаемое значение:
//   Структура:
//     * ДляИБ - Соответствие из КлючИЗначение:
//         ** Ключ - Тип
//         ** Значение - Булево - Истина
//     * ПоТаблицам - Соответствие из КлючИЗначение:
//         ** Ключ - Строка - полное имя объекта метаданных
//         ** Значение - Соответствие из КлючИЗначение:
//              *** Ключ - Тип
//              *** Значение - Булево - Истина
//     * ХешСумма - Строка - контрольная сумма настроек использования ДляИБ и ПоТаблицам.
//     * ПолноеИмяТаблицы - Строка - полное имя таблицы, когда свойство ПоТаблицам
//                                   заполнено только для одной таблицы.
//
Функция ИспользуемыеТипыЗначений(СвойстваВидовДоступа, ПолноеИмя = Неопределено, ВсеВидыДоступаИспользуются = Неопределено, ТолькоХешСумма = Ложь)
	
	ИспользуемыеТипыЗначений = Новый Структура;
	ИспользуемыеТипыЗначений.Вставить("ДляИБ", Новый Соответствие);
	ИспользуемыеТипыЗначений.Вставить("ПоТаблицам", Новый Соответствие);
	ИспользуемыеТипыЗначений.Вставить("ХешСумма", "");
	ИспользуемыеТипыЗначений.Вставить("ПолноеИмяТаблицы", "");
	
	Если ВсеВидыДоступаИспользуются = Ложь Тогда
		Возврат ИспользуемыеТипыЗначений;
	КонецЕсли;
	
	Если ВсеВидыДоступаИспользуются <> Истина Тогда
		Запрос = Новый Запрос;
		Запрос.Текст =
		"ВЫБРАТЬ
		|	ИспользуемыеВидыДоступа.ТипЗначенийДоступа КАК ТипЗначенийДоступа
		|ИЗ
		|	РегистрСведений.ИспользуемыеВидыДоступа КАК ИспользуемыеВидыДоступа
		|ГДЕ
		|	&ОграничиватьДоступНаУровнеЗаписей
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТипЗначенийДоступа
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ИспользуемыеВидыДоступаПоТаблицам.Таблица КАК Таблица,
		|	ИспользуемыеВидыДоступаПоТаблицам.ТипЗначенийДоступа КАК ТипЗначенийДоступа
		|ИЗ
		|	РегистрСведений.ИспользуемыеВидыДоступаПоТаблицам КАК ИспользуемыеВидыДоступаПоТаблицам
		|ГДЕ
		|	ИспользуемыеВидыДоступаПоТаблицам.Таблица = &Таблица
		|
		|УПОРЯДОЧИТЬ ПО
		|	Таблица,
		|	ТипЗначенийДоступа";
		
		Если ПолноеИмя <> Неопределено Тогда
			Идентификатор = ОбщегоНазначения.ИдентификаторОбъектаМетаданных(ПолноеИмя, Ложь);
			Если Идентификатор <> Null Тогда
				Запрос.УстановитьПараметр("Таблица", Идентификатор);
			КонецЕсли;
		КонецЕсли;
		Если ПолноеИмя = Неопределено Или Идентификатор = Null Тогда
			Запрос.Текст = СтрЗаменить(Запрос.Текст,
				"ИспользуемыеВидыДоступаПоТаблицам.Таблица = &Таблица", "ИСТИНА");
		Иначе
			ИспользуемыеТипыЗначений.ПолноеИмяТаблицы = ПолноеИмя;
		КонецЕсли;
		СтрокаОграничениеДоступаВключено =
			?(Константы.ОграничиватьДоступНаУровнеЗаписей.Получить(), "ИСТИНА", "ЛОЖЬ");
		Запрос.Текст = СтрЗаменить(Запрос.Текст, "&ОграничиватьДоступНаУровнеЗаписей",
			СтрокаОграничениеДоступаВключено);
		
		РезультатыЗапроса = Запрос.ВыполнитьПакет();
		Используемые = РезультатыЗапроса[0].Выгрузить();
		ИспользуемыеПоТаблицам = РезультатыЗапроса[1].Выгрузить();
		Если ПолноеИмя = Неопределено Тогда
			Хеширование = Новый ХешированиеДанных(ХешФункция.SHA256);
			Хеширование.Добавить(СтрокаОграничениеДоступаВключено);
			Хеширование.Добавить(СтрокаДанныхДляХеширования(Используемые));
			Хеширование.Добавить(СтрокаДанныхДляХеширования(ИспользуемыеПоТаблицам));
			ИспользуемыеТипыЗначений.ХешСумма = Base64Строка(Хеширование.ХешСумма);
			Если ТолькоХешСумма Тогда
				Возврат ИспользуемыеТипыЗначений;
			КонецЕсли;
		КонецЕсли;
		Таблицы = ИспользуемыеПоТаблицам.Скопировать(, "Таблица");
		Таблицы.Свернуть("Таблица");
		Идентификаторы = Таблицы.ВыгрузитьКолонку("Таблица");
		ОбъектыМетаданных = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(Идентификаторы, Ложь);
		ТекущаяТаблица = Неопределено;
		Для Каждого Строка Из ИспользуемыеПоТаблицам Цикл
			Если ТекущаяТаблица <> Строка.Таблица Тогда
				ОбъектМетаданных = ОбъектыМетаданных.Получить(Строка.Таблица);
				Если ТипЗнч(ОбъектМетаданных) <> Тип("ОбъектМетаданных") Тогда
					Продолжить;
				КонецЕсли;
				ТекущееПолноеИмя = ОбъектМетаданных.ПолноеИмя();
				ТекущаяТаблица = Строка.Таблица;
				ТекущиеИспользуемыеТипы = Новый Соответствие;
				ИспользуемыеТипыЗначений.ПоТаблицам.Вставить(ТекущееПолноеИмя, ТекущиеИспользуемыеТипы);
			КонецЕсли;
			ТекущиеИспользуемыеТипы.Вставить(ТипЗнч(Строка.ТипЗначенийДоступа), Истина);
		КонецЦикла;
	КонецЕсли;
	
	СвойстваВидовДоступаМассив = СвойстваВидовДоступа.Массив; // Массив Из см. СвойстваВидаДоступа
	Для Каждого СвойстваВидаДоступа Из СвойстваВидовДоступаМассив Цикл
		Если ВсеВидыДоступаИспользуются <> Истина
		   И Используемые.Найти(СвойстваВидаДоступа.Ссылка) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		ИспользуемыеТипыЗначений.ДляИБ.Вставить(СвойстваВидаДоступа.ТипЗначений, Истина);
		Для Каждого ОписаниеДополнительногоТипа Из СвойстваВидаДоступа.ДополнительныеТипы Цикл
			ИспользуемыеТипыЗначений.ДляИБ.Вставить(ОписаниеДополнительногоТипа.ТипЗначений, Истина);
		КонецЦикла;
	КонецЦикла;
	
	Возврат ИспользуемыеТипыЗначений;
	
КонецФункции

// Для процедуры ДобавитьПараметрыОграниченияСписка.
//
// Возвращаемое значение:
//  Структура:
//   * Версия - Строка
//   * ВедущиеСписки - Соответствие из КлючИЗначение:
//       ** Ключ     - Строка - полное имя списка
//       ** Значение - см. СвойстваСпискаКакВедущего
//   * ДляПользователей - см. ПараметрыОграниченияПоСтруктуреОграничения
//   * РезультатДляВнешнихПользователей - см. ПараметрыОграниченияПоСтруктуреОграничения
//
Функция РассчитанныеПараметрыОграничения(ПолноеИмя, ОбщийКонтекст, ДействующиеПараметры) Экспорт
	
	ОписаниеОграничения = ОписаниеОграниченияДанных(ОбщийКонтекст, ПолноеИмя);
	
	// Для пользователей.
	СтруктураОграниченияДляПользователей = РассчитаннаяСтруктураОграничения(ПолноеИмя,
		ОписаниеОграничения.Текст, ОписаниеОграничения.ТекстВМодулеМенеджера, Ложь);
	
	ДополнительныйКонтекст = НовыйДополнительныйКонтекст();
	ЗаполнитьЗначенияСвойств(ДополнительныйКонтекст, ДействующиеПараметры.ДополнительныйКонтекст.ДляПользователей);
	ДобавитьДополнительныйКонтекст(ПолноеИмя, ДополнительныйКонтекст, ОписаниеОграничения, Ложь);
	
	РезультатДляПользователей = ПараметрыОграниченияПоСтруктуреОграничения(ПолноеИмя,
		СтруктураОграниченияДляПользователей, Ложь, ОбщийКонтекст, ДополнительныйКонтекст);
	
	// Для внешних пользователей.
	СтруктураОграниченияДляВнешнихПользователей = РассчитаннаяСтруктураОграничения(ПолноеИмя,
		ОписаниеОграничения.ТекстДляВнешнихПользователей, ОписаниеОграничения.ТекстВМодулеМенеджера, Истина);
	
	ДополнительныйКонтекст = НовыйДополнительныйКонтекст();
	ЗаполнитьЗначенияСвойств(ДополнительныйКонтекст, ДействующиеПараметры.ДополнительныйКонтекст.ДляВнешнихПользователей);
	ДобавитьДополнительныйКонтекст(ПолноеИмя, ДополнительныйКонтекст, ОписаниеОграничения, Истина);
	
	РезультатДляВнешнихПользователей = ПараметрыОграниченияПоСтруктуреОграничения(ПолноеИмя,
		СтруктураОграниченияДляВнешнихПользователей, Истина, ОбщийКонтекст, ДополнительныйКонтекст);
	
	// Заполнение параметров на основе параметров обоих видов пользователей.
	Версия = ОбщаяВерсия(ОбщийКонтекст, ПолноеИмя, РезультатДляПользователей.Версия, РезультатДляВнешнихПользователей.Версия);
	УстановитьСвойстваЗаписиКлючейДоступа(РезультатДляПользователей);
	УстановитьСвойстваЗаписиКлючейДоступа(РезультатДляВнешнихПользователей);
	СЗаписьюДвухКлючей = Не РезультатДляПользователей.БезЗаписиКлючейДоступа И Не РезультатДляВнешнихПользователей.БезЗаписиКлючейДоступа;
	БезЗаписиКлючей    =    РезультатДляПользователей.БезЗаписиКлючейДоступа И    РезультатДляВнешнихПользователей.БезЗаписиКлючейДоступа;
	РезультатДляПользователей.Вставить(       "СЗаписьюКлючейДоступаДляПользователейИВнешнихПользователей", СЗаписьюДвухКлючей);
	РезультатДляВнешнихПользователей.Вставить("СЗаписьюКлючейДоступаДляПользователейИВнешнихПользователей", СЗаписьюДвухКлючей);
	РезультатДляПользователей.Вставить(       "БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей", БезЗаписиКлючей);
	РезультатДляВнешнихПользователей.Вставить("БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей", БезЗаписиКлючей);
	
	// Формирование текстов запросов.
	ДобавитьТекстыЗапросовВПараметрыОграничения(РезультатДляПользователей);
	ДобавитьТекстыЗапросовВПараметрыОграничения(РезультатДляВнешнихПользователей);
	
	ВедущиеСписки = Новый Структура;
	ВедущиеСписки.Вставить("ДляПользователей",        РезультатДляПользователей.ВедущиеСписки);
	ВедущиеСписки.Вставить("ДляВнешнихПользователей", РезультатДляВнешнихПользователей.ВедущиеСписки);
	
	Параметры = Новый Структура;
	Параметры.Вставить("Версия",                  Версия);
	Параметры.Вставить("ВедущиеСписки",           ВедущиеСписки);
	Параметры.Вставить("ДляПользователей",        РезультатДляПользователей);
	Параметры.Вставить("ДляВнешнихПользователей", РезультатДляВнешнихПользователей);
	
	Возврат Параметры;
	
КонецФункции

// Для функции ОшибкиОграниченийДоступа.
Функция ОшибкаОграниченияДоступа(ОбщийКонтекст, ПолноеИмя)
	
	ТекстОшибкиДляПользователей = "";
	ТекстОшибкиДляВнешнихПользователей = "";
	
	Попытка
		ОписаниеОграничения = ОписаниеОграниченияДанных(ОбщийКонтекст, ПолноеИмя, Истина);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Возврат ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
	КонецПопытки;
	Если ТипЗнч(ОписаниеОграничения) = Тип("Строка") Тогда
		Возврат ОписаниеОграничения;
	КонецЕсли;
	
	// Для пользователей.
	СтруктураОграниченияДляПользователей = РассчитаннаяСтруктураОграничения(ПолноеИмя,
		ОписаниеОграничения.Текст, ОписаниеОграничения.ТекстВМодулеМенеджера, Ложь, Истина);
	
	Если СтруктураОграниченияДляПользователей <> Неопределено
	   И СтруктураОграниченияДляПользователей.ОписаниеОшибок.ЕстьОшибки Тогда
		
		ТекстОшибкиДляПользователей = ТекстОшибокДляВызоваИсключения(ПолноеИмя,
			СтруктураОграниченияДляПользователей.ОписаниеОшибок, Ложь, ОписаниеОграничения.ТекстВМодулеМенеджера);
	Иначе
		ДополнительныйКонтекст = НовыйДополнительныйКонтекст();
		ДобавитьДополнительныйКонтекст(ПолноеИмя, ДополнительныйКонтекст, ОписаниеОграничения, Ложь);
		
		ОшибкаПриВызовеИсключения = Новый Структура("Текст", Неопределено);
		ДополнительныйКонтекст.Вставить("ОшибкаПриВызовеИсключения", ОшибкаПриВызовеИсключения);
		Попытка
			РезультатДляПользователей = ПараметрыОграниченияПоСтруктуреОграничения(ПолноеИмя,
				СтруктураОграниченияДляПользователей, Ложь, ОбщийКонтекст, ДополнительныйКонтекст);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			Если ЗначениеЗаполнено(ОшибкаПриВызовеИсключения.Текст) Тогда
				ТекстОшибкиДляПользователей = ОшибкаПриВызовеИсключения.Текст;
			Иначе
				ТекстОшибкиДляПользователей = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось сформировать параметры ограничения доступа для пользователей по причине:
					           |%1'"), ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			КонецЕсли;
		КонецПопытки;
		
		Если Не ЗначениеЗаполнено(ТекстОшибкиДляПользователей) Тогда
			РезультатДляПользователей.Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей = Новый Соответствие;
			УстановитьСвойстваЗаписиКлючейДоступа(РезультатДляПользователей);
			РезультатДляПользователей.Вставить("БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей", Ложь);
			РезультатДляПользователей.Контекст.Вставить("ПропуститьПроверкуОпределяемыхТипов");
			Попытка
				ДобавитьТекстыЗапросовВПараметрыОграничения(РезультатДляПользователей);
			Исключение
				ИнформацияОбОшибке = ИнформацияОбОшибке();
				Если ЗначениеЗаполнено(ОшибкаПриВызовеИсключения.Текст) Тогда
					ТекстОшибкиДляПользователей = ОшибкаПриВызовеИсключения.Текст;
				Иначе
					ТекстОшибкиДляПользователей = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не удалось сформировать тексты запросов на основе параметров ограничения доступа
						           |для пользователей по причине:
						           |%1'"), ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
				КонецЕсли;
			КонецПопытки;
		КонецЕсли;
	КонецЕсли;
	
	// Для внешних пользователей.
	СтруктураОграниченияДляВнешнихПользователей = РассчитаннаяСтруктураОграничения(ПолноеИмя,
		ОписаниеОграничения.ТекстДляВнешнихПользователей, ОписаниеОграничения.ТекстВМодулеМенеджера, Истина, Истина);
	
	Если СтруктураОграниченияДляВнешнихПользователей <> Неопределено
	   И СтруктураОграниченияДляВнешнихПользователей.ОписаниеОшибок.ЕстьОшибки Тогда
		
		ТекстОшибкиДляВнешнихПользователей = ТекстОшибокДляВызоваИсключения(ПолноеИмя,
			СтруктураОграниченияДляВнешнихПользователей.ОписаниеОшибок, Истина, ОписаниеОграничения.ТекстВМодулеМенеджера);
	Иначе
		ДополнительныйКонтекст = НовыйДополнительныйКонтекст();
		ДобавитьДополнительныйКонтекст(ПолноеИмя, ДополнительныйКонтекст, ОписаниеОграничения, Истина);
		
		ОшибкаПриВызовеИсключения = Новый Структура("Текст", Неопределено);
		ДополнительныйКонтекст.Вставить("ОшибкаПриВызовеИсключения", ОшибкаПриВызовеИсключения);
		Попытка
			РезультатДляВнешнихПользователей = ПараметрыОграниченияПоСтруктуреОграничения(ПолноеИмя,
				СтруктураОграниченияДляВнешнихПользователей, Истина, ОбщийКонтекст, ДополнительныйКонтекст);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			Если ЗначениеЗаполнено(ОшибкаПриВызовеИсключения.Текст) Тогда
				ТекстОшибкиДляВнешнихПользователей = ОшибкаПриВызовеИсключения.Текст;
			Иначе
				ТекстОшибкиДляВнешнихПользователей = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось сформировать параметры ограничения доступа для внешних пользователей по причине:
					           |%1'"), ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			КонецЕсли;
		КонецПопытки;
		
		Если Не ЗначениеЗаполнено(ТекстОшибкиДляВнешнихПользователей) Тогда
			РезультатДляВнешнихПользователей.Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей = Новый Соответствие;
			УстановитьСвойстваЗаписиКлючейДоступа(РезультатДляВнешнихПользователей);
			РезультатДляВнешнихПользователей.Вставить("БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей", Ложь);
			РезультатДляВнешнихПользователей.Контекст.Вставить("ПропуститьПроверкуОпределяемыхТипов");
			Попытка
				ДобавитьТекстыЗапросовВПараметрыОграничения(РезультатДляВнешнихПользователей);
			Исключение
				ИнформацияОбОшибке = ИнформацияОбОшибке();
				Если ЗначениеЗаполнено(ОшибкаПриВызовеИсключения.Текст) Тогда
					ТекстОшибкиДляВнешнихПользователей = ОшибкаПриВызовеИсключения.Текст;
				Иначе
					ТекстОшибкиДляВнешнихПользователей = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не удалось сформировать тексты запросов на основе параметров ограничения доступа
						           |для внешних пользователей по причине:
						           |%1'"), ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
				КонецЕсли;
			КонецПопытки;
		КонецЕсли;
	КонецЕсли;
	
	Возврат СокрЛП(ТекстОшибкиДляПользователей
		+ Символы.ПС + Символы.ПС + ТекстОшибкиДляВнешнихПользователей);
	
КонецФункции

// Для функции РезультатПроверкиОграниченияДоступа.
Функция РезультатПроверкиОграниченияДоступаОбъекта(ПолноеИмя, ДополнительныеПараметры)
	
	Параметры = Новый Структура;
	Параметры.Вставить("Текст", Неопределено);
	Параметры.Вставить("ТекстДляВнешнихПользователей", Неопределено);
	Параметры.Вставить("УчитыватьЗависимости", Ложь);
	Параметры.Вставить("ВсеВидыДоступаИспользуются", Истина);
	
	Если ТипЗнч(ДополнительныеПараметры) = Тип("Структура") Тогда
		ЗаполнитьЗначенияСвойств(Параметры, ДополнительныеПараметры);
	КонецЕсли;
	
	ДляПользователей        = СтруктураРезультатаПроверкиОграниченияДляВидаПользователей();
	ДляВнешнихПользователей = СтруктураРезультатаПроверкиОграниченияДляВидаПользователей();
	
	Результат = Новый Структура;
	Результат.Вставить("ОшибкаОписанияОграничения", "");
	Результат.Вставить("ТекстВМодулеМенеджера",     Неопределено);
	Результат.Вставить("ДляПользователей",          ДляПользователей);
	Результат.Вставить("ДляВнешнихПользователей",   ДляВнешнихПользователей);
	Результат.Вставить("НастройкиВнедрения",        Новый Структура);
	
	ОбщийКонтекст = Неопределено;
	Попытка
		ОбщийКонтекст = ОбщийКонтекстРасчетаПараметровОграничения(, Параметры.ВсеВидыДоступаИспользуются, Ложь);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Результат.ОшибкаОписанияОграничения = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
	КонецПопытки;
	
	Если ОбщийКонтекст = Неопределено Тогда
		Возврат Результат;
	КонецЕсли;
	
	УчетЗависимостейДоступен = Истина;
	СпискиСОграничением = Неопределено;
	Попытка
		СпискиСОграничением = УправлениеДоступомСлужебныйПовтИсп.СпискиСОграничением();
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Результат.ОшибкаОписанияОграничения = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		УчетЗависимостейДоступен = Ложь;
	КонецПопытки;
	
	Если СпискиСОграничением <> Неопределено Тогда
		СпискиСОграничением = Новый Соответствие(СпискиСОграничением);
	Иначе
		СпискиСОграничением = Новый Соответствие;
		УчетЗависимостейДоступен = Ложь;
	КонецЕсли;
	ОбщийКонтекст.Вставить("СпискиСОграничением", СпискиСОграничением);
	
	ОписаниеОграничения = Неопределено;
	Попытка
		ОписаниеОграничения = ОписаниеОграниченияДанных(ОбщийКонтекст, ПолноеИмя, Истина);
	Исключение
		Результат.ОшибкаОписанияОграничения = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		УчетЗависимостейДоступен = Ложь;
	КонецПопытки;
	Если ТипЗнч(ОписаниеОграничения) = Тип("Строка") Тогда
		Результат.ОшибкаОписанияОграничения = ОписаниеОграничения;
		ОписаниеОграничения = Неопределено;
	КонецЕсли;
	
	Если ОписаниеОграничения <> Неопределено Тогда
		Результат.ТекстВМодулеМенеджера                    = ОписаниеОграничения.ТекстВМодулеМенеджера;
		ДляПользователей.ОграничениеВМодуле                = ОписаниеОграничения.Текст;
		ДляПользователей.ПоВладельцуБезЗаписиКлючейДоступа = ОписаниеОграничения.ПоВладельцуБезЗаписиКлючейДоступа;
		ДляВнешнихПользователей.ОграничениеВМодуле         = ОписаниеОграничения.ТекстДляВнешнихПользователей;
		ДляВнешнихПользователей.ПоВладельцуБезЗаписиКлючейДоступа =
			ОписаниеОграничения.ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей;
		
		Если Параметры.Текст = Неопределено Тогда
			ДляПользователей.ПроверяемоеОграничение = ДляПользователей.ОграничениеВМодуле;
		Иначе
			ДляПользователей.ПроверяемоеОграничение = Параметры.Текст;
			ОписаниеОграничения.Текст               = Параметры.Текст;
		КонецЕсли;
		
		Если Параметры.ТекстДляВнешнихПользователей = Неопределено Тогда
			ДляВнешнихПользователей.ПроверяемоеОграничение = ДляВнешнихПользователей.ОграничениеВМодуле;
		Иначе
			ДляВнешнихПользователей.ПроверяемоеОграничение   = Параметры.ТекстДляВнешнихПользователей;
			ОписаниеОграничения.ТекстДляВнешнихПользователей = Параметры.ТекстДляВнешнихПользователей;
		КонецЕсли;
	КонецЕсли;
	
	Если СпискиСОграничением.Получить(ПолноеИмя) = Неопределено Тогда
		СпискиСОграничением.Вставить(ПолноеИмя, Истина);
		Результат.ТекстВМодулеМенеджера = Неопределено;
	КонецЕсли;
	
	Контекст = Новый Структура;
	Контекст.Вставить("ПолноеИмя",                ПолноеИмя);
	Контекст.Вставить("ОбщийРезультат",           Результат);
	Контекст.Вставить("ОбщийКонтекст",            ОбщийКонтекст);
	Контекст.Вставить("ОписаниеОграничения",      ОписаниеОграничения);
	Контекст.Вставить("УчитыватьЗависимости",     Параметры.УчитыватьЗависимости);
	Контекст.Вставить("УчетЗависимостейДоступен", УчетЗависимостейДоступен);
	Контекст.Вставить("ВерсииОграниченийСписков", Новый Соответствие);
	Контекст.Вставить("ВедущиеСписки",            Новый Соответствие);
	Контекст.Вставить("ДополнительныйКонтекст",   Новый Структура("ДляПользователей, ДляВнешнихПользователей"));
	
	ПроверитьОграничениеДляВидаПользователей(Контекст, ДляПользователей, Ложь,
		Контекст.ДополнительныйКонтекст.ДляПользователей);
	
	ПроверитьОграничениеДляВидаПользователей(Контекст, ДляВнешнихПользователей, Истина,
		Контекст.ДополнительныйКонтекст.ДляВнешнихПользователей);
	
	Если Не Контекст.УчетЗависимостейДоступен Тогда
		Возврат Результат;
	КонецЕсли;
	
	Если Параметры.УчитыватьЗависимости Тогда
		ОбщийКонтекст.Вставить("СпециальноеПолноеИмя", ПолноеИмя);
		ОбщийКонтекст.Вставить("СпециальноеОписаниеОграничения", ОписаниеОграничения);
		ОбщийКонтекст.Вставить("СпискиСОграничением", Новый ФиксированноеСоответствие(СпискиСОграничением));
		НовыеХранимыеПараметры = ХранимыеПараметрыОграниченияДоступа(ОбщийКонтекст);
		ДействующиеПараметры = Новый Структура(НовыеХранимыеПараметры.ДляЗаписиОбъектовИПроверкиПрав.Получить()); // см. НоваяСтруктураХранимыхПараметровЗаписи
		Если ДействующиеПараметры.ВерсииОграниченийСписков.Получить(ПолноеИмя) = Неопределено Тогда
			Контекст.ВерсииОграниченийСписков.Вставить(ПолноеИмя, Истина);
		Иначе
			Контекст.ВерсииОграниченийСписков.Вставить(ПолноеИмя,
				ДействующиеПараметры.ВерсииОграниченийСписков.Получить(ПолноеИмя));
		КонецЕсли;
		УстановитьОграничениеПоВладельцуИспользуется(ДляПользователей,
			ПолноеИмя, ДействующиеПараметры.ДополнительныйКонтекст.ДляПользователей);
		УстановитьОграничениеПоВладельцуИспользуется(ДляВнешнихПользователей,
			ПолноеИмя, ДействующиеПараметры.ДополнительныйКонтекст.ДляВнешнихПользователей);
	Иначе
		Контекст.ВерсииОграниченийСписков.Вставить(ПолноеИмя, Истина);
		ДействующиеПараметры = Новый Структура;
		ДействующиеПараметры.Вставить("ДополнительныйКонтекст", Контекст.ДополнительныйКонтекст);
	КонецЕсли;
	ДействующиеПараметры.Вставить("ВерсииОграниченийСписков", Контекст.ВерсииОграниченийСписков);
	ДействующиеПараметры.Вставить("ВедущиеСписки",            Контекст.ВедущиеСписки);
	
	Настройки = НастройкиВнедрения(ДействующиеПараметры);
	
	ТипыТаблицПоИменам = УправлениеДоступомСлужебныйПовтИсп.СинтаксисЯзыка().ТипыТаблиц.ПоИменам;
	УстановитьНастройкиВнедрения(Результат.НастройкиВнедрения, Настройки, ТипыТаблицПоИменам);
	
	Результат.ДляПользователей.ОграничениеВРолях = Настройки.ОграниченияВРолях.ДляПользователей.Получить(
		ПолноеИмяXML(ПолноеИмя, ТипыТаблицПоИменам));
	
	Результат.ДляВнешнихПользователей.ОграничениеВРолях = Настройки.ОграниченияВРолях.ДляВнешнихПользователей.Получить(
		ПолноеИмяXML(ПолноеИмя, ТипыТаблицПоИменам));
	
	Возврат Результат;
	
КонецФункции

// Для функции РезультатПроверкиОграниченияДоступаОбъекта
Процедура УстановитьОграничениеПоВладельцуИспользуется(Результат, ПолноеИмя, ДополнительныйКонтекст)
	
	Свойства = ДополнительныйКонтекст.СвойстваОграниченияСписков.Получить(ПолноеИмя);
	Если Свойства = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Результат.ОграничениеПоВладельцуИспользуется =
		Свойства.ПолеВладельца <> Неопределено И Не Свойства.ПолеВладельца.Отключено;
	
КонецПроцедуры

// Для функции РезультатПроверкиОграниченияДоступаОбъекта.
Процедура УстановитьНастройкиВнедрения(НастройкиВнедрения, Данные, ТипыТаблицПоИменам)
	
	ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Истина,
		Данные.ЗначенияДоступа, "ЗначениеДоступа");
	
	ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Истина,
		Данные.ВладельцыЗначенийКлючейДоступа.Ссылки, "ВладелецЗначенийКлючейДоступа");
	
	ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Ложь,
		Данные.ВладельцыЗначенийКлючейДоступа.Объекты, "ВладелецЗначенийКлючейДоступаОбъект");
	
	ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Ложь,
		Данные.ВладельцыЗначенийКлючейДоступа.Документы, "ВладелецЗначенийКлючейДоступаДокумент");
	
	ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Ложь,
		Данные.ВладельцыЗначенийКлючейДоступа.НаборыЗаписей, "ВладелецЗначенийКлючейДоступаНаборЗаписей");
	
	ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Ложь,
		Данные.ВладельцыЗначенийКлючейДоступа.НаборыЗаписейРегистраРасчета,
			"ВладелецЗначенийКлючейДоступаНаборЗаписейРегистраРасчета");
	
	ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Ложь,
		Данные.ВладельцыЗначенийКлючейДоступа.НаборыЗаписейРегистраРасчета,
			"ВладелецЗначенийКлючейДоступаНаборЗаписейРегистраРасчета");
	
	НастройкиВнедрения.Вставить("ПолеРегистраКлючейДоступаКРегистрам", "");
	НастройкиВнедрения.Вставить("ТипыИзмеренийОтдельногоРегистраКлючей", Неопределено);
	Для Каждого КлючИЗначение Из Данные.ТипыИзмеренийРегистровКлючей Цикл
		Если СтрНайти(КлючИЗначение.Ключ, ".") > 0 Тогда
			Продолжить;
		КонецЕсли;
		ИмяРегистраКлючей = КлючИЗначение.Ключ;
		
		Если ИмяРегистраКлючей = "КлючиДоступаКРегистрам" Тогда
			ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, Истина,
				КлючИЗначение.Значение.ИменаТипов, "ПолеРегистраКлючейДоступаКРегистрам");
		Иначе
			ИзмеренияРегистра = Метаданные.РегистрыСведений[ИмяРегистраКлючей].Измерения;
			СписокТипов = "";
			НомерПоля = 1;
			Для Каждого ОписаниеПолейРегистра Из КлючИЗначение.Значение.ПоляРегистров Цикл
				Прервать;
			КонецЦикла;
			Для Каждого ОписаниеПоля Из ОписаниеПолейРегистра.Значение Цикл
				ПолеРегистра = ИзмеренияРегистра.Найти("Поле" + НомерПоля);
				ОписаниеТипов = ?(ПолеРегистра = Неопределено, Новый ОписаниеТипов, ПолеРегистра.Тип);
				СписокТипов = СписокТипов + ?(СписокТипов = "", "", Символы.ПС)
					+ НСтр("ru = '- для измерения'") + " " + "Поле" + НомерПоля + ":" + Символы.ПС
					+ "	" + ТекстСОтступом(СписокТиповИзМассива(ОписаниеПоля.Тип.Типы(),
						Истина, ТипыТаблицПоИменам, ОписаниеТипов), "	");
				НомерПоля = НомерПоля + 1;
			КонецЦикла;
			НастройкиВнедрения.Вставить("ТипыИзмеренийОтдельногоРегистраКлючей",
				Новый Структура("ИмяРегистраСведений, ТипыИзмерений", ИмяРегистраКлючей, СписокТипов));
		КонецЕсли;
	КонецЦикла;
	
	НастройкиВнедрения.Вставить("ПредопределенныйИдентификатор", Неопределено);
	Для Каждого КлючИЗначение Из Данные.ПредопределенныеИдентификаторы Цикл
		ЧастиИмени = СтрРазделить(КлючИЗначение.Ключ, ".", Ложь);
		УжеДобавлен = Метаданные.Справочники[ЧастиИмени[0]].ПолучитьИменаПредопределенных().Найти(ЧастиИмени[1]) <> Неопределено;
		НастройкиВнедрения.Вставить("ПредопределенныйИдентификатор",
			Новый Структура("ИмяСправочника, ИмяПредопределенного", ЧастиИмени[0],
				 "- " + ЧастиИмени[1] + ?(УжеДобавлен, " (" + НСтр("ru = 'уже добавлен'") + ")", "")));
		Прервать;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры УстановитьНастройкиВнедрения.
Процедура ДобавитьТипыТребуемыеВОпределяемомТипе(НастройкиВнедрения, ТипыТаблицПоИменам, ТипыСсылок, ИменаТипов, ИмяОпределяемогоТипа)
	
	НастройкиВнедрения.Вставить(ИмяОпределяемогоТипа, "");
	Если ИменаТипов.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	НастройкиВнедрения[ИмяОпределяемогоТипа] = СписокТиповИзМассива(ИменаТипов,
		ТипыСсылок, ТипыТаблицПоИменам, Метаданные.ОпределяемыеТипы[ИмяОпределяемогоТипа].Тип);
	
КонецПроцедуры

// Для процедур УстановитьНастройкиВнедрения, ДобавитьТипыТребуемыеВОпределяемомТипе.
Функция СписокТиповИзМассива(ИменаТипов, ТипыСсылок, ТипыТаблицПоИменам, ОписаниеТипов)
	
	СписокТипов = "";
	Для Каждого ИмяТипа Из ИменаТипов Цикл
		Если ТипЗнч(ИмяТипа) = Тип("Тип") Тогда
			Тип = ИмяТипа;
		Иначе
			Тип = Тип(ИмяТипа);
		КонецЕсли;
		ОбъектМетаданных = Метаданные.НайтиПоТипу(Тип);
		ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
		
		СписокТипов = СписокТипов + ?(СписокТипов = "", "", Символы.ПС)
			+ "- " + ?(ТипыСсылок, ИмяТипаСсылки(ПолноеИмя, ТипыТаблицПоИменам),
				ИмяТипаОбъектаИлиНабораЗаписей(ПолноеИмя, ТипыТаблицПоИменам));
		
		Если ОписаниеТипов.СодержитТип(Тип) Тогда
			СписокТипов = СписокТипов + " (" + НСтр("ru = 'уже добавлен'") + ")";
		КонецЕсли;
	КонецЦикла;
	
	Возврат СписокТипов;
	
КонецФункции

// Для функции РезультатПроверкиОграниченияДоступаОбъекта.
Функция СтруктураРезультатаПроверкиОграниченияДляВидаПользователей()
	
	Свойства = Новый Структура;
	Свойства.Вставить("ПроверяемоеОграничение");
	Свойства.Вставить("ОписаниеОшибок");
	Свойства.Вставить("ОшибкаФормированияПараметровОграничения");
	Свойства.Вставить("ОшибкаФормированияТекстовЗапросов");
	Свойства.Вставить("ОграничениеПоВладельцуВозможно");
	Свойства.Вставить("ОграничениеПоВладельцуИспользуется");
	Свойства.Вставить("ОграничениеВРолях");
	Свойства.Вставить("ОграничениеВМодуле");
	Свойства.Вставить("ПоВладельцуБезЗаписиКлючейДоступа");
	
	Возврат Свойства;
	
КонецФункции

// Для функции РезультатПроверкиОграниченияДоступаОбъекта.
Процедура ПроверитьОграничениеДляВидаПользователей(Контекст, Результат, ДляВнешнихПользователей, ДополнительныйКонтекст)
	
	ТекстОграничения = ?(ДляВнешнихПользователей, Контекст.ОбщийРезультат.ДляВнешнихПользователей,
		Контекст.ОбщийРезультат.ДляПользователей).ПроверяемоеОграничение;
	
	СтруктураОграничения = РассчитаннаяСтруктураОграничения(Контекст.ПолноеИмя,
		ТекстОграничения, Контекст.ОбщийРезультат.ТекстВМодулеМенеджера, ДляВнешнихПользователей, Истина);
	
	Если СтруктураОграничения <> Неопределено
	   И СтруктураОграничения.ОписаниеОшибок.ЕстьОшибки Тогда
		
		Результат.ОписаниеОшибок = СтруктураОграничения.ОписаниеОшибок;
		Контекст.УчетЗависимостейДоступен = Ложь;
		Возврат;
	КонецЕсли;
	
	ДополнительныйКонтекст = НовыйДополнительныйКонтекст();
	ДобавитьДополнительныйКонтекст(Контекст.ПолноеИмя,
		ДополнительныйКонтекст, Контекст.ОписаниеОграничения, ДляВнешнихПользователей);
	
	ОшибкаПриВызовеИсключения = Новый Структура("Текст", Неопределено);
	ДополнительныйКонтекст.Вставить("ОшибкаПриВызовеИсключения", ОшибкаПриВызовеИсключения);
	
	Попытка
		ПараметрыОграничения = ПараметрыОграниченияПоСтруктуреОграничения(Контекст.ПолноеИмя,
			СтруктураОграничения, ДляВнешнихПользователей, Контекст.ОбщийКонтекст, ДополнительныйКонтекст);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ПараметрыОграничения = Неопределено;
		Если ЗначениеЗаполнено(ОшибкаПриВызовеИсключения.Текст) Тогда
			Результат.ОшибкаФормированияПараметровОграничения = ОшибкаПриВызовеИсключения.Текст;
		Иначе
			Результат.ОшибкаФормированияПараметровОграничения =
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось сформировать параметры ограничения доступа по причине:
					           |%1'"), ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
		КонецЕсли;
		Контекст.УчетЗависимостейДоступен = Ложь;
	КонецПопытки;
	
	Если ПараметрыОграничения <> Неопределено Тогда
		Результат.ОграничениеПоВладельцуВозможно = ПараметрыОграничения.ПолеВладельца <> Неопределено;
		ПараметрыОграничения.Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей = Новый Соответствие;
		УстановитьСвойстваЗаписиКлючейДоступа(ПараметрыОграничения);
		ПараметрыОграничения.Вставить("БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей", Ложь);
		Для Каждого КлючИЗначение Из ПараметрыОграничения.ВедущиеСписки.ПоЗначениямПолей Цикл
			Контекст.ВедущиеСписки.Вставить(КлючИЗначение.Ключ, Неопределено);
		КонецЦикла;
		Для Каждого КлючИЗначение Из ПараметрыОграничения.ВедущиеСписки.ПоКлючамДоступа Цикл
			Контекст.ВерсииОграниченийСписков.Вставить(КлючИЗначение.Ключ, Неопределено);
		КонецЦикла;
		ПараметрыОграничения.Контекст.Вставить("ПропуститьПроверкуОпределяемыхТипов");
		Попытка
			ДобавитьТекстыЗапросовВПараметрыОграничения(ПараметрыОграничения);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			Если ЗначениеЗаполнено(ОшибкаПриВызовеИсключения.Текст) Тогда
				Результат.ОшибкаФормированияТекстовЗапросов = ОшибкаПриВызовеИсключения.Текст;
			Иначе
				Результат.ОшибкаФормированияТекстовЗапросов =
					СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не удалось сформировать тексты запросов на основе параметров ограничения доступа по причине:
						           |%1'"), ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			КонецЕсли;
			Контекст.УчетЗависимостейДоступен = Ложь;
		КонецПопытки;
	КонецЕсли;
	
	Если Контекст.УчитыватьЗависимости Тогда
		Возврат;
	КонецЕсли;
	
	СокращенныеСвойства = Новый Структура;
	СокращенныеСвойства.Вставить("ДоступЗапрещен", Ложь);
	СокращенныеСвойства.Вставить("ПолеВладельца", Неопределено);
	СокращенныеСвойства.Вставить("ОпорныеПоля", Неопределено);
	СокращенныеСвойства.Вставить("ИмяОтдельногоРегистраКлючей", Неопределено);
	ДополнительныйКонтекст.СвойстваОграниченияСписков.Вставить(Контекст.ПолноеИмя, СокращенныеСвойства);
	
	Если Не Контекст.УчетЗависимостейДоступен Тогда
		Возврат;
	КонецЕсли;
	
	ЗаполнитьЗначенияСвойств(СокращенныеСвойства, ПараметрыОграничения);
	
КонецПроцедуры

// Для функций ХранимыеПараметрыОграниченияДоступа, РассчитанныеПараметрыОграничения.
Функция ОбщаяВерсия(ОбщийКонтекст, ПолноеИмя, ВерсияДляПользователей, ВерсияДляВнешнихПользователей)
	
	Если ОбщийКонтекст.СпискиСОграничением.Получить(ПолноеИмя) = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Возврат Строка(ВерсияДляПользователей) + Символы.ПС + Строка(ВерсияДляВнешнихПользователей);
	
КонецФункции

// Для функции РассчитанныеПараметрыОграничения.
Процедура УстановитьСвойстваЗаписиКлючейДоступа(Результат)
	
	БезЗаписиКлючей = Ложь;
	СЗаписьюКлючаДляЗависимыхСписковБезКлючей = Ложь;
	
	Если Результат.ОграничениеОтключено
	 Или Результат.ДоступЗапрещен
	 Или Результат.ИспользуетсяОграничениеПоВладельцу Тогда
		
		Если Результат.ИспользуетсяОграничениеПоВладельцу
		 Или Результат.Контекст.БезОбъектаМетаданных
		 Или Результат.Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей.Получить(
		 		Результат.Список) = Неопределено Тогда
			
			БезЗаписиКлючей = Истина;
		Иначе
			СЗаписьюКлючаДляЗависимыхСписковБезКлючей = Истина;
		КонецЕсли;
	КонецЕсли;
	
	Результат.Вставить("БезЗаписиКлючейДоступа", БезЗаписиКлючей);
	Результат.Вставить("СЗаписьюКлючаДоступаДляЗависимыхСписковБезКлючей",
		СЗаписьюКлючаДляЗависимыхСписковБезКлючей);
	
КонецПроцедуры

// Для функций РассчитанныеПараметрыОграничения, ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//   Структура:
//     * Текст - Строка
//     * ТекстДляВнешнихПользователей - Строка
//     * ПоВладельцуБезЗаписиКлючейДоступа - Булево
//                                         - Неопределено
//     * ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей - Булево
//                                                                - Неопределено
//     * ТекстВМодулеМенеджера - Булево
//
Функция ОписаниеОграниченияДанных(ОбщийКонтекст, ПолноеИмя, БезВызоваИсключения = Ложь)
	
	Ограничение = Новый Структура;
	Ограничение.Вставить("Текст", "");
	Ограничение.Вставить("ТекстДляВнешнихПользователей", "");
	Ограничение.Вставить("ПоВладельцуБезЗаписиКлючейДоступа", Неопределено);
	Ограничение.Вставить("ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей", Неопределено);
	Ограничение.Вставить("ТекстВМодулеМенеджера", Ложь);
	
	ТекстВМодулеМенеджера = ОбщийКонтекст.СпискиСОграничением.Получить(ПолноеИмя);
	Если ТекстВМодулеМенеджера = Неопределено Тогда
		Возврат Ограничение;
	КонецЕсли;
	
	Ограничение.ТекстВМодулеМенеджера = ТекстВМодулеМенеджера;
	
	Если ОбщегоНазначения.ПодсистемаСуществует("СтандартныеПодсистемы.РаботаСФайлами") Тогда
		МодульРаботаСФайламиСлужебный = ОбщегоНазначения.ОбщийМодуль("РаботаСФайламиСлужебный");
		ЭтоСправочникФайлов = МодульРаботаСФайламиСлужебный.ЭтоСправочникФайловИлиВерсийФайлов(ПолноеИмя);
	Иначе
		ЭтоСправочникФайлов = Ложь;
	КонецЕсли;
	
	Если ЭтоЖурналДокументов(ПолноеИмя) Или ЭтоСправочникФайлов Тогда
		// Для журналов документов ограничение должно быть
		// по документу-владельцу без записи ключей доступа, если не требуется другое.
		// Аналогично (по умолчанию) для справочников файлов и версий файлов.
		Ограничение.ПоВладельцуБезЗаписиКлючейДоступа = Истина;
		Ограничение.ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей = Истина;
	КонецЕсли;
	
	Если ТекстВМодулеМенеджера Тогда
		Менеджер = ОбщегоНазначения.МенеджерОбъектаПоПолномуИмени(ПолноеИмя);
		
		Попытка
			Менеджер.ПриЗаполненииОграниченияДоступа(Ограничение);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '""%1"" указан, как список с ограничением доступа в процедуре
				           |%2 общего модуля %3.
				           |
				           |Некорректно указано ограничение доступа этого списка в модуле менеджера
				           |в процедуре %4 по причине:
				           |
				           |%5'"),
				ПолноеИмя,
				"ПриЗаполненииСписковСОграничениемДоступа",
				"УправлениеДоступомПереопределяемый",
				"ПриЗаполненииОграниченияДоступа",
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			
			Если БезВызоваИсключения Тогда
				Возврат ТекстОшибки;
			КонецЕсли;
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
	Иначе
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя);
		Попытка
			УправлениеДоступомПереопределяемый.ПриЗаполненииОграниченияДоступа(ОбъектМетаданных, Ограничение);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '""%1"" указан, как список с ограничением доступа в процедуре
				           |%2 общего модуля %3.
				           |
				           |Некорректно указано ограничение доступа этого списка в общем модуле %4
				           |в процедуре %5 по причине:
				           |
				           |%6'"),
				ПолноеИмя,
				"ПриЗаполненииСписковСОграничениемДоступа",
				"УправлениеДоступомПереопределяемый",
				"УправлениеДоступомПереопределяемый",
				"ПриЗаполненииОграниченияДоступа",
				ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке));
			
			Если БезВызоваИсключения Тогда
				Возврат ТекстОшибки;
			КонецЕсли;
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
	КонецЕсли;
	
	Если ЭтоЖурналДокументов(ПолноеИмя)
	   И (    Ограничение.ПоВладельцуБезЗаписиКлючейДоступа <> Истина
		  Или Ограничение.ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей <> Истина) Тогда
		
		Если ВариантВстроенногоЯзыкаРусский() Тогда
			ШаблонОграничения =
				"РазрешитьЧтениеИзменение
				|ГДЕ
				|	ЧтениеОбъектаРазрешено(Ссылка)"; // @Non-NLS
		Иначе
			ШаблонОграничения =
				"AllowReadWrite
				|WHERE
				|	ObjectReadingAllowed(Ref)";
		КонецЕсли;
		
		Если ТекстВМодулеМенеджера Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '""%1"" указан, как список с ограничением доступа в процедуре
				           |%2 общего модуля %3.
				           |
				           |Некорректно указано ограничение доступа этого списка в модуле менеджера
				           |в процедуре %4 по причине:
				           |
				           |Для журналов документов не поддерживается ограничение, как для регистров,
				           |то есть, кроме варианта ограничения по владельцу без записи ключей доступа:
				           |
				           |%5'"),
				ПолноеИмя,
				"ПриЗаполненииСписковСОграничениемДоступа",
				"УправлениеДоступомПереопределяемый",
				"ПриЗаполненииОграниченияДоступа",
				ШаблонОграничения);
		Иначе
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '""%1"" указан, как список с ограничением доступа в процедуре
				           |%2 общего модуля %3.
				           |
				           |Некорректно указано ограничение доступа этого списка в общем модуле %4
				           |в процедуре %5 по причине:
				           |
				           |Для журналов документов не поддерживается ограничение, как для регистров,
				           |то есть, кроме варианта ограничения по владельцу без записи ключей доступа:
				           |
				           |%6'"),
				ПолноеИмя,
				"ПриЗаполненииСписковСОграничениемДоступа",
				"УправлениеДоступомПереопределяемый",
				"УправлениеДоступомПереопределяемый",
				"ПриЗаполненииОграниченияДоступа",
				ШаблонОграничения);
		КонецЕсли;
		Если БезВызоваИсключения Тогда
			Возврат ТекстОшибки;
		КонецЕсли;
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Возврат Ограничение;
	
КонецФункции

// Для функции ОписаниеОграниченияДанных.
Функция ЭтоЖурналДокументов(ПолноеИмя)
	
	Возврат СтрНачинаетсяС(ВРег(ПолноеИмя), ВРег("ЖурналДокументов.")) // @Non-NLS
	    Или СтрНачинаетсяС(ВРег(ПолноеИмя), ВРег("DocumentJournal."));
	
КонецФункции

// Для функций РассчитанныеПараметрыОграничения и ПараметрыОграниченияДляВидаПользователей.
//
// Возвращаемое значение:
//   см. СтруктураОграничения
//
Функция РассчитаннаяСтруктураОграничения(ПолноеИмя, ТекстОграничения, ТекстВМодулеМенеджера, ДляВнешнихПользователей, БезИсключения = Ложь)
	
	Если Не ЗначениеЗаполнено(ТекстОграничения) Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	РазобранноеОграничение = РазобранноеОграничение(ПолноеИмя, ТекстОграничения);
	
	ПроверитьТаблицыПоляИТипыПолей(РазобранноеОграничение);
	
	СтруктураОграничения = СтруктураОграничения(РазобранноеОграничение);
	
	Если БезИсключения Или Не СтруктураОграничения.ОписаниеОшибок.ЕстьОшибки Тогда
		Возврат СтруктураОграничения;
	КонецЕсли;
	
	ТекстОшибок = ТекстОшибокДляВызоваИсключения(ПолноеИмя,
		СтруктураОграничения.ОписаниеОшибок, ДляВнешнихПользователей, ТекстВМодулеМенеджера);
	
	ВызватьИсключение ТекстОшибок;
	
КонецФункции

// Для функций СвойстваСпискаКакВедущего, ПараметрыОграничения и процедур УстановкаПараметровСеанса,
// ЗаполнитьПараметрыОграничения, ДобавитьПараметрыОграниченияСписка.
//
// Возвращаемое значение:
//   см. НоваяСтруктураХранимыхПараметровЗаписи
//
Функция ДействующиеПараметрыОграниченияДоступа(ИдентификаторТранзакции, ОбщийКонтекст,
			Обновить, УстановкаПараметровСеансаДляШаблонов = Ложь,
			УстановкаПараметровДляОтчетаПраваДоступа = Ложь, ЕстьИзменения = Ложь) Экспорт
	
	Если Обновить Тогда
		ВерсияПараметров = НоваяВерсияПараметровОграниченияДоступа(ОбщийКонтекст, ЕстьИзменения);
		УстановитьВерсиюПараметров(ВерсияПараметров, ИдентификаторТранзакции, ОбщийКонтекст);
		ПараметрыОграниченияДоступа = ПараметрыСеанса.ПараметрыОграниченияДоступа; // См. СеансовыеПараметрыОграниченияДоступа
		Возврат ПараметрыОграниченияДоступа.Параметры;
	КонецЕсли;
	
	ТекущиеПараметры = ПараметрыСеанса.ПараметрыОграниченияДоступа; // См. СеансовыеПараметрыОграниченияДоступа
	
	Если Не ТекущиеПараметры.Свойство("Параметры") Или ТекущиеПараметры.Параметры = Неопределено Тогда
		ТекущиеПараметры = Новый Структура("Версия, ХешСумма", "", ""); // См. СеансовыеПараметрыОграниченияДоступа
		ИдентификаторыТранзакции = Новый Соответствие;
	Иначе
		ИдентификаторыТранзакции = КэшПараметровОграничения().ИдентификаторыТранзакции;
	КонецЕсли;
	
	Если ТранзакцияАктивна()
	   И ИдентификаторыТранзакции.Получить(ИдентификаторТранзакции) <> Неопределено Тогда
		
		Возврат ТекущиеПараметры.Параметры;
	КонецЕсли;
	
	Пока Истина Цикл
		ОписаниеВерсии = ОписаниеПоследнейВерсии();
		Если ТекущиеПараметры.Версия   = ОписаниеВерсии.Версия
		   И ТекущиеПараметры.ХешСумма = ОписаниеВерсии.ХешСумма Тогда
			Прервать;
		КонецЕсли;
		Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
			Прервать;
		КонецЕсли;
		Если ЗавершеныЗаданияЗаписиНовойВерсииПараметровОграниченияДоступа() Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если Не УстановкаПараметровСеансаДляШаблонов
	   И Не УстановкаПараметровДляОтчетаПраваДоступа
	   И ТекущиеПараметры.Версия   = ОписаниеВерсии.Версия
	   И ТекущиеПараметры.ХешСумма = ОписаниеВерсии.ХешСумма Тогда
		
		ОбновитьИдентификаторыТранзакции(ИдентификаторТранзакции);
		Возврат ТекущиеПараметры.Параметры;
	КонецЕсли;
	
	// Параметры в базе данных отличаются от параметров в памяти.
	Если ЗначениеЗаполнено(ОписаниеВерсии.Версия) Тогда
		ВерсияПараметров = ВерсияПараметров(ОписаниеВерсии.Версия,
			УстановкаПараметровСеансаДляШаблонов, УстановкаПараметровДляОтчетаПраваДоступа);
	Иначе
		ВерсияПараметров = НоваяВерсияПараметровОграниченияДоступа(ОбщийКонтекст);
	КонецЕсли;
	
	УстановитьВерсиюПараметров(ВерсияПараметров, ИдентификаторТранзакции, ОбщийКонтекст,
		УстановкаПараметровСеансаДляШаблонов);
	
	ПараметрыОграниченияДоступа = ПараметрыСеанса.ПараметрыОграниченияДоступа; // См. СеансовыеПараметрыОграниченияДоступа
	Возврат ПараметрыОграниченияДоступа.Параметры;
	
КонецФункции

// Для отчета ПраваДоступа.
//
// Возвращаемое значение:
//  Структура:
//   * ДляПользователей - Строка
//   * ДляВнешнихПользователей - Строка
//
Функция ВсеВидыОграниченийПравДляОтчетаПраваДоступа()
	
	Кэш = КэшПараметровОграничения();
	
	Если Кэш.ВидыОграниченийПравДляПользователей <> Неопределено
	   И Кэш.ВидыОграниченийПравДляВнешнихПользователей <> Неопределено Тогда
		Возврат Новый Структура("ДляПользователей, ДляВнешнихПользователей",
			Кэш.ВидыОграниченийПравДляПользователей,
			Кэш.ВидыОграниченийПравДляВнешнихПользователей);
	КонецЕсли;
	
	ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь, Ложь, Истина);
	
	Возврат ВсеВидыОграниченийПравДляОтчетаПраваДоступа();
	
КонецФункции

// Для функции НоваяВерсияПараметровОграниченияДоступа.
Функция ЗаписьПараметровОграниченияДоступаВТекущемСеансе()
	
	УстановитьПривилегированныйРежим(Истина);
	ТекущиеПараметры = ПараметрыСеанса.ПараметрыОграниченияДоступа;
	УстановитьПривилегированныйРежим(Ложь);
	
	Возврат ТекущиеПараметры.Свойство("ЗаписьПараметровОграниченияДоступаВТекущемСеансе");
	
КонецФункции

// Для функций ДействующиеПараметрыОграниченияДоступа, НоваяВерсияПараметровОграниченияДоступа.
//
// Возвращаемое значение:
//  Структура:
//   * Версия                   - Число
//                              - Неопределено
//   * ХешСумма                 - Строка
//                              - Неопределено
//   * ДатаСоздания             - Дата
//                              - Неопределено
//   * ВерсииПараметровШаблонов - ХранилищеЗначения
//                              - Неопределено
//
Функция ОписаниеПоследнейВерсии(ПрочитатьВерсииПараметровШаблонов = Ложь)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ПараметрыОграниченияДоступа.Версия КАК Версия,
	|	ПараметрыОграниченияДоступа.ХешСумма КАК ХешСумма,
	|	ПараметрыОграниченияДоступа.ДатаСоздания КАК ДатаСоздания,
	|	ПараметрыОграниченияДоступа.ВерсииПараметровШаблонов КАК ВерсииПараметровШаблонов
	|ИЗ
	|	РегистрСведений.ПараметрыОграниченияДоступа КАК ПараметрыОграниченияДоступа
	|
	|УПОРЯДОЧИТЬ ПО
	|	ПараметрыОграниченияДоступа.Версия УБЫВ";
	
	Если Не ПрочитатьВерсииПараметровШаблонов Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ПараметрыОграниченияДоступа.ВерсииПараметровШаблонов", "Неопределено");
	КонецЕсли;
	
	Выборка = Запрос.Выполнить().Выбрать();
	Выборка.Следующий();
	
	Результат = Новый Структура("Версия, ХешСумма, ДатаСоздания, ВерсииПараметровШаблонов");
	ЗаполнитьЗначенияСвойств(Результат, Выборка);
	
	Возврат Результат;
	
КонецФункции

// Для функции ДействующиеПараметрыОграниченияДоступа.
Функция ВерсияПараметров(Версия, УстановкаПараметровСеансаДляШаблонов, УстановкаПараметровДляОтчетаПраваДоступа)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ПараметрыОграниченияДоступа.Версия КАК Версия,
	|	ПараметрыОграниченияДоступа.ХешСумма КАК ХешСумма,
	|	ПараметрыОграниченияДоступа.ДатаСоздания КАК ДатаСоздания,
	|	ПараметрыОграниченияДоступа.ДляШаблоновВСеансахПользователей КАК ДляШаблоновВСеансахПользователей,
	|	ПараметрыОграниченияДоступа.ДляШаблоновВСеансахВнешнихПользователей КАК ДляШаблоновВСеансахВнешнихПользователей,
	|	ПараметрыОграниченияДоступа.ДляЗаписиОбъектовИПроверкиПрав КАК ДляЗаписиОбъектовИПроверкиПрав,
	|	ПараметрыОграниченияДоступа.ВерсииПараметровШаблонов КАК ВерсииПараметровШаблонов,
	|	ПараметрыОграниченияДоступа.ДляОтчетаПоПравамДоступа КАК ДляОтчетаПоПравамДоступа
	|ИЗ
	|	РегистрСведений.ПараметрыОграниченияДоступа КАК ПараметрыОграниченияДоступа
	|ГДЕ
	|	ПараметрыОграниченияДоступа.Версия = &Версия";
	
	Если УстановкаПараметровСеансаДляШаблонов Или УстановкаПараметровДляОтчетаПраваДоступа Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ПараметрыОграниченияДоступа.ДляЗаписиОбъектовИПроверкиПрав", "Неопределено");
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ПараметрыОграниченияДоступа.ВерсииПараметровШаблонов", "Неопределено");
	КонецЕсли;
	Если Не УстановкаПараметровДляОтчетаПраваДоступа Тогда
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ПараметрыОграниченияДоступа.ДляОтчетаПоПравамДоступа", "Неопределено");
	КонецЕсли;
		
	Если УстановкаПараметровСеансаДляШаблонов
	 Или ПараметрыСеанса.ПараметрыОграниченияДоступа.Свойство("ПараметрыСеансаДляШаблоновУстановлены") Тогда
		
		Запрос.Текст = СтрЗаменить(Запрос.Текст, ?(Пользователи.ЭтоСеансВнешнегоПользователя(),
			"ПараметрыОграниченияДоступа.ДляШаблоновВСеансахПользователей",
			"ПараметрыОграниченияДоступа.ДляШаблоновВСеансахВнешнихПользователей"), "Неопределено");
	Иначе
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ПараметрыОграниченияДоступа.ДляШаблоновВСеансахПользователей", "Неопределено");
		Запрос.Текст = СтрЗаменить(Запрос.Текст,
			"ПараметрыОграниченияДоступа.ДляШаблоновВСеансахВнешнихПользователей", "Неопределено");
	КонецЕсли;
	
	Запрос.УстановитьПараметр("Версия", Версия);
	
	Выборка = Запрос.Выполнить().Выбрать();
	Выборка.Следующий();
	
	Возврат Выборка;
	
КонецФункции

// Для функции ДействующиеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//   см. ХранимыеПараметрыОграниченияДоступа
//
Функция НоваяВерсияПараметровОграниченияДоступа(ОбщийКонтекст, ЕстьИзменения = Ложь)
	
	Попытка
		ПроверитьАктуальностьМетаданных();
	Исключение
		Если СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке()) Тогда
			ЗапланироватьОбновлениеПараметровОграниченияДоступа(
				"НоваяВерсияПараметровОграниченияДоступа", Истина);
		КонецЕсли;
		ВызватьИсключение;
	КонецПопытки;
	
	УдалитьСтрокиВерсииХранимыхПараметров = Ложь;
	
	Если ТипЗнч(ОбщийКонтекст) <> Тип("Структура")
	 Или ЗначениеЗаполнено(ОбщийКонтекст.ИспользуемыеТипыЗначений.ПолноеИмяТаблицы) Тогда
		ОбновитьПовторноИспользуемыеЗначения();
		ОбщийКонтекст = ОбщийКонтекстРасчетаПараметровОграничения();
	КонецЕсли;
	
	Если РегистрироватьСтрокуВерсииПараметровОграниченияДоступа() Тогда
		Если Не ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
			ОбщийКонтекст.Вставить("СтрокиВерсииХранимыхПараметров");
			УдалитьСтрокиВерсииХранимыхПараметров = Истина;
		КонецЕсли;
	КонецЕсли;
	
	ВерсииОграниченийСписков = Новый Соответствие;
	ХранимыеПараметры = ХранимыеПараметрыОграниченияДоступа(ОбщийКонтекст, ВерсииОграниченийСписков);
	ВерсииОграниченийСписков.Вставить("Справочник.НаборыГруппДоступа", "1");
	
	ПараметрыЗаписи = Новый Структура;
	ПараметрыЗаписи.Вставить("ВерсииОграниченийСписков", ВерсииОграниченийСписков);
	ПараметрыЗаписи.Вставить("ХранимыеПараметры",        ХранимыеПараметры);
	ПараметрыЗаписи.Вставить("ИдентификаторДоступа",     ИдентификаторДоступа());
	
	Если ОбщийКонтекст.Свойство("СпискиСУстаревшимиВариантамиДоступа") Тогда
		ПараметрыЗаписи.Вставить("СпискиСУстаревшимиВариантамиДоступа",
			ОбщийКонтекст.СпискиСУстаревшимиВариантамиДоступа);
	Иначе
		ПараметрыЗаписи.Вставить("СпискиСУстаревшимиВариантамиДоступа");
	КонецЕсли;
	
	Если ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
		ПараметрыЗаписи.Вставить("СтрокиВерсии", ОбщийКонтекст.СтрокиВерсииХранимыхПараметров);
	КонецЕсли;
	
	Если Не ТранзакцияАктивна()
	 Или МонопольныйРежим()
	 Или ОбщийКонтекст.Свойство("ЗаписатьВТекущейТранзакции")
	 Или ОбщегоНазначения.ИнформационнаяБазаФайловая()
	 Или СтандартныеПодсистемыСервер.ЭтоРазделенныйРежимСеансаБезРазделителей()
	 Или ЗаписьПараметровОграниченияДоступаВТекущемСеансе() Тогда
		
		НомерПопыткиБлокировки = 0;
		Пока Истина Цикл
			НомерПопыткиБлокировки = НомерПопыткиБлокировки + 1;
			ЭтоОшибкаБлокировки = Ложь;
			Попытка
				ОписаниеВерсии = ОписаниеНовойВерсииПараметровОграниченияДоступа(ПараметрыЗаписи,, ЭтоОшибкаБлокировки);
			Исключение
				Если ЭтоОшибкаБлокировки
				   И НомерПопыткиБлокировки < 3
				   И Не ТранзакцияАктивна() Тогда
					Продолжить;
				КонецЕсли;
				Если СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке()) Тогда
					ЗапланироватьОбновлениеПараметровОграниченияДоступа(
						"НоваяВерсияПараметровОграниченияДоступа", Истина);
				КонецЕсли;
				ВызватьИсключение;
			КонецПопытки;
			Прервать;
		КонецЦикла;
	Иначе
		ПараметрыЗаписиВХранилище = Новый ХранилищеЗначения(ПараметрыЗаписи);
		ОписаниеВерсии = Неопределено;
		Пока Истина Цикл
			ОписаниеВерсии = ОписаниеНовойВерсииПараметровОграниченияДоступа(
				ПараметрыЗаписиВХранилище.Получить(), Истина);
			Если ОписаниеВерсии <> Неопределено Тогда
				Прервать;
			КонецЕсли;
			Если ЗавершеныЗаданияЗаписиНовойВерсииПараметровОграниченияДоступа() Тогда
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если ОписаниеВерсии = Неопределено Тогда
			ИмяПроцедуры = ИмяПроцедурыЗаданияЗаписиНовойВерсииПараметровОграниченияДоступа();
			АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено);
			ПараметрыПроцедуры = Новый Массив;
			ПараметрыПроцедуры.Добавить(АдресРезультата);
			ПараметрыПроцедуры.Добавить(ПараметрыЗаписиВХранилище);
			ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
			НаименованиеЗадания = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Управление доступом: Запись новой версии параметров ограничения доступа (из сеанса %1 от %2)'",
					ОбщегоНазначения.КодОсновногоЯзыка()),
				Формат(ТекущийСеанс.НомерСеанса, "ЧГ="),
				Формат(ТекущийСеанс.НачалоСеанса, "ДЛФ=DT"));
			ФоновоеЗадание = ФоновыеЗадания.Выполнить(ИмяПроцедуры, ПараметрыПроцедуры,, НаименованиеЗадания);
			ФоновоеЗадание = ФоновоеЗадание.ОжидатьЗавершенияВыполнения(60);
			Результат = ПолучитьИзВременногоХранилища(АдресРезультата);
			ЗаголовокОшибки = НСтр("ru = 'Не удалось записать новую версию параметров ограничения доступа по причине:'");
			Если ФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
				ФоновоеЗадание.Отменить();
				ТекстОшибки = ЗаголовокОшибки + Символы.ПС
					+ НСтр("ru = 'Фоновое задание выполняется более 60 секунд, поэтому отменено.'");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			Если ФоновоеЗадание.Состояние = СостояниеФоновогоЗадания.Отменено Тогда
				ТекстОшибки = ЗаголовокОшибки + Символы.ПС
					+ НСтр("ru = 'Фоновое задание отменено администратором.'");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			Если ФоновоеЗадание.Состояние <> СостояниеФоновогоЗадания.Завершено Тогда
				Если ТипЗнч(ФоновоеЗадание.ИнформацияОбОшибке) = Тип("ИнформацияОбОшибке") Тогда
					ТекстОшибки = ЗаголовокОшибки + Символы.ПС
						+ ОбработкаОшибок.ПодробноеПредставлениеОшибки(ФоновоеЗадание.ИнформацияОбОшибке);
				Иначе
					ТекстОшибки = ЗаголовокОшибки + Символы.ПС
						+ НСтр("ru = 'Фоновое задание завершилось аварийно.'");
				КонецЕсли;
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			Если ТипЗнч(Результат) <> Тип("Структура") Тогда
				ТекстОшибки = ЗаголовокОшибки + Символы.ПС
					+ НСтр("ru = 'Фоновое задание не вернуло результат.'");
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			Если Результат.ТребуетсяПерезапускСеанса Тогда
				ЗапланироватьОбновлениеПараметровОграниченияДоступа(
					"НоваяВерсияПараметровОграниченияДоступа", Истина);
				ПроверитьАктуальностьМетаданных();
				СтандартныеПодсистемыСервер.УстановитьТребуетсяПерезапускСеанса(Результат.ТекстОшибки);
				ВызватьИсключение Результат.ТекстОшибки;
			КонецЕсли;
			Если ЗначениеЗаполнено(Результат.ТекстОшибки) Тогда
				ТекстОшибки = ЗаголовокОшибки + Символы.ПС + Результат.ТекстОшибки;
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
			ОписаниеВерсии = Результат.ОписаниеВерсии;
		КонецЕсли;
	КонецЕсли;
	
	Если ОбщийКонтекст.Свойство("СпискиСУстаревшимиВариантамиДоступа") Тогда
		ОбщийКонтекст.СпискиСУстаревшимиВариантамиДоступа =
			ОписаниеВерсии.СпискиСУстаревшимиВариантамиДоступа;
	КонецЕсли;
	
	Если УдалитьСтрокиВерсииХранимыхПараметров Тогда
		ОбщийКонтекст.Удалить("СтрокиВерсииХранимыхПараметров");
		
	ИначеЕсли ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
		ОбщийКонтекст.СтрокиВерсииХранимыхПараметров = ОписаниеВерсии.СтрокиВерсии;
	КонецЕсли;
	
	Если ОписаниеВерсии.ЕстьИзменения Тогда
		ЕстьИзменения = Истина;
	КонецЕсли;
	
	Для Каждого КлючИЗначение Из ОписаниеВерсии.ЗаполняемыеСвойстваВерсии Цикл
		ХранимыеПараметры.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	
	Возврат ХранимыеПараметры;
	
КонецФункции

// Для функций НоваяВерсияПараметровОграниченияДоступа и
// ДействующиеПараметрыОграниченияДоступа.
//
Функция ЗавершеныЗаданияЗаписиНовойВерсииПараметровОграниченияДоступа()
	
	ОтборЗаданий = Новый Структура("ИмяМетода, Состояние",
		ИмяПроцедурыЗаданияЗаписиНовойВерсииПараметровОграниченияДоступа(),
		СостояниеФоновогоЗадания.Активно);
	
	НайденныеЗадания = ФоновыеЗадания.ПолучитьФоновыеЗадания(ОтборЗаданий);
	
	Если НайденныеЗадания.Количество() = 0 Тогда
		Возврат Истина;
	КонецЕсли;
	
	ФоновыеЗадания.ОжидатьЗавершенияВыполнения(НайденныеЗадания, 1);
	
	Возврат Ложь;
	
КонецФункции

// Для функций НоваяВерсияПараметровОграниченияДоступа и
// ЗавершеныЗаданияЗаписиНовойВерсииПараметровОграниченияДоступа.
//
Функция ИмяПроцедурыЗаданияЗаписиНовойВерсииПараметровОграниченияДоступа()
	
	Возврат "УправлениеДоступомСлужебный.ЗаписатьНовуюВерсиюПараметровОграниченияДоступаВФоне";
	
КонецФункции

// Для функции НоваяВерсияПараметровОграниченияДоступа.
Процедура ЗаписатьНовуюВерсиюПараметровОграниченияДоступаВФоне(АдресРезультата, ХранилищеПараметров) Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("ОписаниеВерсии", Новый Структура);
	Результат.Вставить("ТекстОшибки", "");
	Результат.Вставить("ТребуетсяПерезапускСеанса", Ложь);
	
	УстановитьПривилегированныйРежим(Истина);
	Попытка
		Параметры = ХранилищеПараметров.Получить();
		Если Параметры.ИдентификаторДоступа = ИдентификаторДоступа() Тогда
			Результат.ОписаниеВерсии = ОписаниеНовойВерсииПараметровОграниченияДоступа(Параметры);
		Иначе
			Результат.ТекстОшибки = НСтр("ru = 'Ошибка проверки доступа.'");
		КонецЕсли;
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Если СтандартныеПодсистемыСервер.ТребуетсяПерезапускСеанса(Результат.ТекстОшибки) Тогда
			Результат.ТребуетсяПерезапускСеанса = Истина;
		КонецЕсли;
		Если Не Результат.ТребуетсяПерезапускСеанса
		 Или Не СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке) Тогда
			Результат.ТекстОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
		КонецЕсли;
	КонецПопытки;
	УстановитьПривилегированныйРежим(Ложь);
	
	ПоместитьВоВременноеХранилище(Результат, АдресРезультата);
	
КонецПроцедуры

// Для функции НоваяВерсияПараметровОграниченияДоступа и
// процедуры ЗаписатьНовуюВерсиюПараметровОграниченияДоступаВФоне.
//
Функция ИдентификаторДоступа()
	
	Возврат ПоследнееОбновлениеДоступа().ИдентификаторДоступа;
	
КонецФункции

// Для функции НоваяВерсияПараметровОграниченияДоступа и
// процедуры ЗаписатьНовуюВерсиюПараметровОграниченияДоступаВФоне.
// 
Функция ОписаниеНовойВерсииПараметровОграниченияДоступа(Параметры, БезЗаписи = Ложь, ЭтоОшибкаБлокировки = Ложь)
	
	Запись = НоваяТаблицаРегистраСведенийПараметрыОграниченияДоступа().Добавить();
	ЗаполнитьЗначенияСвойств(Запись, Параметры.ХранимыеПараметры);
	
	ЗаполняемыеСвойстваВерсии = Новый Структура("Версия, ДатаСоздания, ХешСумма,
	|ВерсииПараметровШаблонов, ХешСуммаПостоянныхПараметров, ХешСуммаПараметровШаблонов,
	|ДляШаблоновВСеансахПользователей, ДляШаблоновВСеансахВнешнихПользователей");
	
	Результат = Новый Структура;
	Результат.Вставить("ЕстьИзменения", Ложь);
	Результат.Вставить("ЗаполняемыеСвойстваВерсии", ЗаполняемыеСвойстваВерсии);
	Результат.Вставить("СпискиСУстаревшимиВариантамиДоступа",
		Параметры.СпискиСУстаревшимиВариантамиДоступа);
	Параметры.Вставить("СпискиСНовымОсновнымВариантомДоступа", Новый Массив);
	Если Параметры.Свойство("СтрокиВерсии") Тогда
		Результат.Вставить("СтрокиВерсии", Параметры.СтрокиВерсии);
	КонецЕсли;
	
	Если БезЗаписи Тогда
		ОписаниеВерсии = ОписаниеПоследнейВерсии(Истина);
		ЗаполнитьПараметрыДляШаблонов(Запись, Параметры, ОписаниеВерсии);
		Если ОписаниеВерсии.ХешСумма <> Запись.ХешСумма Тогда
			Возврат Неопределено;
		КонецЕсли;
	Иначе
		НачатьТранзакцию();
		Попытка
			Если ОбщегоНазначения.ИнформационнаяБазаФайловая() Тогда
				БлокировкаДанных = Новый БлокировкаДанных;
				БлокировкаДанных.Добавить("РегистрСведений.ПараметрыОграниченияДоступа");
				Попытка
					БлокировкаДанных.Заблокировать();
				Исключение
					ЭтоОшибкаБлокировки = Истина;
					ВызватьИсключение;
				КонецПопытки;
				БлокировкаДанных = Новый БлокировкаДанных;
				БлокировкаДанных.Добавить("РегистрСведений.ОбновлениеКлючейДоступаКДанным");
				БлокировкаДанных.Добавить("РегистрСведений.ОбновлениеКлючейДоступаПользователей");
				Попытка
					БлокировкаДанных.Заблокировать();
				Исключение
					ЭтоОшибкаБлокировки = Истина;
					ВызватьИсключение;
				КонецПопытки;
			КонецЕсли;
			ОписаниеВерсии = ОписаниеПоследнейВерсии(Истина);
			Пока Истина Цикл
				ЗаполнитьПараметрыДляШаблонов(Запись, Параметры, ОписаниеВерсии);
				Если ОписаниеВерсии.ХешСумма = Запись.ХешСумма Тогда
					Прервать;
				КонецЕсли;
				
				ПроверитьАктуальностьМетаданных();
				
				Если ЗначениеЗаполнено(ОписаниеВерсии.Версия) Тогда
					НоваяВерсия = ОписаниеВерсии.Версия + 1;
				Иначе
					НоваяВерсия = 1;
				КонецЕсли;
				
				БлокировкаДанных = Новый БлокировкаДанных;
				ЭлементБлокировки = БлокировкаДанных.Добавить("РегистрСведений.ПараметрыОграниченияДоступа");
				ЭлементБлокировки.УстановитьЗначение("Версия", НоваяВерсия);
				БлокировкаДанных.Заблокировать();
				НовоеОписаниеВерсии = ОписаниеПоследнейВерсии(Истина);
				
				Если ОписаниеВерсии.Версия <> НовоеОписаниеВерсии.Версия
				 Или НовоеОписаниеВерсии.Версия = НоваяВерсия Тогда
					
					ОписаниеВерсии = НовоеОписаниеВерсии;
					Продолжить;
				КонецЕсли;
				
				ПроверитьАктуальностьМетаданных();
				
				Запись.Версия = НоваяВерсия;
				
				НаборЗаписей = СлужебныйНаборЗаписей(РегистрыСведений.ПараметрыОграниченияДоступа);
				ЗаполнитьЗначенияСвойств(НаборЗаписей.Добавить(), Запись);
				Запись = НаборЗаписей[0];
				НаборЗаписей.Отбор.Версия.Установить(НоваяВерсия);
				ЗаполнитьЗначенияСвойств(ЗаполняемыеСвойстваВерсии, Запись);
				
				ЗапланироватьОбновлениеДоступаПриИзмененииПараметров(ОписаниеВерсии.Версия, Параметры);
				НаборЗаписей.Записать();
				Результат.ЕстьИзменения = Истина;
				
				Если РегистрироватьСтрокуВерсииПараметровОграниченияДоступа() Тогда
					ЗарегистрироватьСтрокуВерсииПараметровОграниченияДоступа(НаборЗаписей[0],
						Параметры.СтрокиВерсии);
				КонецЕсли;
				
				Прервать;
			КонецЦикла;
			ЗафиксироватьТранзакцию();
		Исключение
			ОтменитьТранзакцию();
			ВызватьИсключение;
		КонецПопытки;
	КонецЕсли;
	
	Если Не Результат.ЕстьИзменения Тогда
		ЗаполнитьЗначенияСвойств(ЗаполняемыеСвойстваВерсии, ОписаниеВерсии,
			"Версия, ДатаСоздания, ХешСумма, ВерсииПараметровШаблонов");
		ЗаполнитьЗначенияСвойств(ЗаполняемыеСвойстваВерсии, Запись,
			"ХешСуммаПостоянныхПараметров, ХешСуммаПараметровШаблонов,
			|ДляШаблоновВСеансахПользователей, ДляШаблоновВСеансахВнешнихПользователей");
	ИначеЕсли Не ТранзакцияАктивна() Тогда
		ИмяПараметра = "СтандартныеПодсистемы.УправлениеДоступом.ДатаПроверкиПараметровОграниченияДоступа";
		СтандартныеПодсистемыСервер.УстановитьПараметрРаботыРасширения(ИмяПараметра, ТекущаяДатаСеанса(), Истина);
		УстановитьОбновлениеДоступа(Истина);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

// Возвращаемое значение:
//  ТаблицаЗначений:
//   * Версия                                  - Число
//   * ХешСумма                                - Строка
//   * ХешСуммаПостоянныхПараметров            - Строка
//   * ДляЗаписиОбъектовИПроверкиПрав          - ХранилищеЗначения
//   * ДляОтчетаПоПравамДоступа                - ХранилищеЗначения
//   * ХешСуммаПараметровШаблонов              - Строка
//   * ДляШаблоновВСеансахПользователей        - ХранилищеЗначения
//   * ДляШаблоновВСеансахВнешнихПользователей - ХранилищеЗначения
//   * ВерсииПараметровШаблонов                - ХранилищеЗначения
//   * ДатаСоздания                            - Дата
//
Функция НоваяТаблицаРегистраСведенийПараметрыОграниченияДоступа()
	
	ТипВерсии   = Новый ОписаниеТипов("Число",,, Новый КвалификаторыЧисла(15, 0, ДопустимыйЗнак.Неотрицательный));
	ТипХешСуммы = Новый ОписаниеТипов("Строка",,,, Новый КвалификаторыСтроки(48, ДопустимаяДлина.Переменная));
	
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Версия",                                  ТипВерсии);
	Таблица.Колонки.Добавить("ХешСумма",                                ТипХешСуммы);
	Таблица.Колонки.Добавить("ХешСуммаПостоянныхПараметров",            ТипХешСуммы);
	Таблица.Колонки.Добавить("ДляЗаписиОбъектовИПроверкиПрав",          Новый ОписаниеТипов("ХранилищеЗначения"));
	Таблица.Колонки.Добавить("ДляОтчетаПоПравамДоступа",                Новый ОписаниеТипов("ХранилищеЗначения"));
	Таблица.Колонки.Добавить("ХешСуммаПараметровШаблонов",              ТипХешСуммы);
	Таблица.Колонки.Добавить("ДляШаблоновВСеансахПользователей",        Новый ОписаниеТипов("ХранилищеЗначения"));
	Таблица.Колонки.Добавить("ДляШаблоновВСеансахВнешнихПользователей", Новый ОписаниеТипов("ХранилищеЗначения"));
	Таблица.Колонки.Добавить("ВерсииПараметровШаблонов",                Новый ОписаниеТипов("ХранилищеЗначения"));
	Таблица.Колонки.Добавить("ДатаСоздания",                            Новый ОписаниеТипов("Дата"));
	
	Возврат Таблица;
	
КонецФункции

// Для функции ОписаниеНовойВерсииПараметровОграниченияДоступа.
Процедура ЗапланироватьОбновлениеДоступаПриИзмененииПараметров(СтараяВерсия, Параметры)
	
	НедоступныеСписки = Новый Массив;
	Списки = СпискиСИзменениемВерсий(СтараяВерсия,
		Параметры.ВерсииОграниченийСписков, НедоступныеСписки);
	
	ПараметрыПланирования = ПараметрыПланированияОбновленияДоступа();
	ПараметрыПланирования.ВерсииОграниченийСписков = Параметры.ВерсииОграниченийСписков;
	ПараметрыПланирования.ЭтоПродолжениеОбновления = Истина;
	ПараметрыПланирования.Описание = "НоваяВерсияПараметровОграниченияДоступа";
	
	ЗапланироватьОбновлениеДоступа(Списки, ПараметрыПланирования);
	ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов = Истина;
	ЗапланироватьОбновлениеДоступа(Списки, ПараметрыПланирования);
	
	Если ЗначениеЗаполнено(НедоступныеСписки) Тогда
		ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов = Ложь;
		ЗапланироватьОбновлениеДоступа(НедоступныеСписки, ПараметрыПланирования);
		ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов = Истина;
		ЗапланироватьОбновлениеДоступа(НедоступныеСписки, ПараметрыПланирования);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Параметры.СпискиСНовымОсновнымВариантомДоступа) Тогда
		ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов = Ложь;
		ЗапланироватьОбновлениеДоступа(Параметры.СпискиСНовымОсновнымВариантомДоступа,
			ПараметрыПланирования);
		ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов = Истина;
		ЗапланироватьОбновлениеДоступа(Параметры.СпискиСНовымОсновнымВариантомДоступа,
			ПараметрыПланирования);
	КонецЕсли;
	Если ЗначениеЗаполнено(Параметры.СпискиСУстаревшимиВариантамиДоступа) Тогда
		ПараметрыПланирования.ЭтоОбработкаУстаревшихЭлементов = Истина;
		ЗапланироватьОбновлениеДоступа(Параметры.СпискиСУстаревшимиВариантамиДоступа,
			ПараметрыПланирования);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений и
// функции НоваяВерсияПараметровОграниченияДоступа.
//
Процедура ПроверитьАктуальностьМетаданных() Экспорт
	
	КонфигурацияИзменена = КонфигурацияБазыДанныхИзмененаДинамически();
	РасширенияИзменены = Справочники.ВерсииРасширений.РасширенияИзмененыДинамически();
	
	Если Не КонфигурацияИзменена И Не РасширенияИзменены Тогда
		Возврат;
	КонецЕсли;
	
	Попытка
		Попытка
			Если КонфигурацияИзменена Тогда
				СтандартныеПодсистемыСервер.ПотребоватьПерезапускСеансаПоПричинеДинамическогоОбновленияВерсииПрограммы();
			ИначеЕсли РасширенияИзменены Тогда
				СтандартныеПодсистемыСервер.ПотребоватьПерезапускСеансаПоПричинеДинамическогоОбновленияРасширенийПрограммы();
			КонецЕсли;
		Исключение
			Если ЭтоСеансФоновогоЗадания()
			 Или СтандартныеПодсистемыСервер.ЭтоРазделенныйРежимСеансаБезРазделителей() Тогда
				ВызватьИсключение;
			КонецЕсли;
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось проверить или обновить права доступа по причине:
				           |%1
				           |
				           |Повторите операцию через минуту, и если проблема останется, перезапустите сеанс.'"),
						   ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
			ВызватьИсключение ТекстОшибки;
		КонецПопытки;
	Исключение
		ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
		СтандартныеПодсистемыСервер.УстановитьТребуетсяПерезапускСеанса(ТекстОшибки);
		ПриОшибкеПроверкиАктуальностиМетаданных(ТекстОшибки);
		ВызватьИсключение;
	КонецПопытки;
	
КонецПроцедуры

// Для процедуры ПроверитьАктуальностьМетаданных
Функция ЭтоСеансФоновогоЗадания()
	
	Если ТекущийРежимЗапуска() <> Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
	ТекущееФоновоеЗадание = ТекущийСеанс.ПолучитьФоновоеЗадание();
	Если ТекущееФоновоеЗадание = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Для процедуры УстановитьОбновлениеДоступа
Функция ЭтоСеансФоновогоОбновленияДоступа()
	
	Если ТекущийРежимЗапуска() <> Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
	ТекущееФоновоеЗадание = ТекущийСеанс.ПолучитьФоновоеЗадание();
	Если ТекущееФоновоеЗадание = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Возврат ТекущееФоновоеЗадание.ИмяМетода = ИмяМетодаПотокаОбновленияДоступа()
	    Или ТекущееФоновоеЗадание.ИмяМетода = ИмяМетодаЗаданияОбновленияДоступа();
	
КонецФункции

// Для функции НоваяВерсияПараметровОграниченияДоступа.
Процедура ЗарегистрироватьСтрокуВерсииПараметровОграниченияДоступа(Запись, СтрокиВерсии)
	
	Состав = Новый Массив;
	Состав.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		"Версия = %1
		|ДатаСоздания = %2
		|ХешСумма = %3
		|ХешСуммаПостоянныхПараметров = %4
		|ХешСуммаПараметровШаблонов = %5",
		Запись.Версия,
		Формат(Запись.ДатаСоздания, "ДЛФ=DT"),
		Запись.ХешСумма,
		Запись.ХешСуммаПостоянныхПараметров,
		Запись.ХешСуммаПараметровШаблонов));
		
	Для Каждого Строка Из СтрокиВерсии.СтрокиВерсийСписков Цикл
		Состав.Добавить(СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			"[Список = %1, ДляВнешнихПользователей = %2, Версия = %3]
			|%4",
			Строка.Список,
			?(Строка.ДляВнешнихПользователей, "Да", "Нет"),
			Строка.Версия,
			Строка.СтрокаВерсии));
	КонецЦикла;
	
	Состав.Добавить("[ВсеВерсииСтрокой]
	|" + СтрокиВерсии.ВсеВерсииСтрокой);
	
	Состав.Добавить("[ВидыОграниченийПравДляПользователейСтрокой]
	|" + СтрокиВерсии.ВидыОграниченийПравДляПользователейСтрокой);
	
	Состав.Добавить("[ВидыОграниченийПравДляВнешнихПользователейСтрокой]
	|" + СтрокиВерсии.ВидыОграниченийПравДляВнешнихПользователейСтрокой);
	
	Состав.Добавить("[СпискиСОграничениемПоПолямВСеансахПользователей]
	|" + СтрокиВерсии.СпискиСОграничениемПоПолямВСеансахПользователей);
	
	Состав.Добавить("[СпискиСОграничениемПоПолямВСеансахВнешнихПользователей]
	|" + СтрокиВерсии.СпискиСОграничениемПоПолямВСеансахВнешнихПользователей);
	
	Комментарий = СтрСоединить(Состав, Символы.ПС);
	
	ЗаписьЖурналаРегистрации(
		НСтр("ru = 'Управление доступом.Строка версии параметров ограничения доступа'",
		     ОбщегоНазначения.КодОсновногоЯзыка()),
		УровеньЖурналаРегистрации.Информация,
		Метаданные.РегистрыСведений.ПараметрыОграниченияДоступа,,
		Комментарий,
		РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
	
КонецПроцедуры

// Для функции НоваяВерсияПараметровОграниченияДоступа.
Функция СпискиСИзменениемВерсий(Версия, НовыеВерсииОграниченийСписков, НедоступныеСписки)
	
	Если Не ЗначениеЗаполнено(Версия) Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ВерсияПараметров = ВерсияПараметров(Версия, Ложь, Ложь);
	ПараметрыЗаписиХранилище = ВерсияПараметров.ДляЗаписиОбъектовИПроверкиПрав;
	
	Если ТипЗнч(ПараметрыЗаписиХранилище) <> Тип("ХранилищеЗначения") Тогда
		Возврат Неопределено;
	КонецЕсли;
	ПараметрыЗаписи = ЗначениеИзХранилища(ПараметрыЗаписиХранилище);
	
	Если ТипЗнч(ПараметрыЗаписи) <> Тип("ФиксированнаяСтруктура")
	 Или Не ПараметрыЗаписи.Свойство("ВерсииОграниченийСписков")
	 Или ТипЗнч(ПараметрыЗаписи.ВерсииОграниченийСписков) <> Тип("ФиксированноеСоответствие") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Таблица = Новый ТаблицаЗначений;
	Таблица.Колонки.Добавить("Список",       Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("Версия",       Новый ОписаниеТипов("Строка"));
	Таблица.Колонки.Добавить("ВидИзменения", Новый ОписаниеТипов("Число"));
	
	Для Каждого КлючИЗначение Из ПараметрыЗаписи.ВерсииОграниченийСписков Цикл
		Строка = Таблица.Добавить();
		Строка.Список = КлючИЗначение.Ключ;
		Строка.Версия = СтрПолучитьСтроку(КлючИЗначение.Значение, 1)
			+ Символы.ПС + СтрПолучитьСтроку(КлючИЗначение.Значение, 2);
		Строка.ВидИзменения = -1;
	КонецЦикла;
	
	Для Каждого КлючИЗначение Из НовыеВерсииОграниченийСписков Цикл
		Строка = Таблица.Добавить();
		Строка.Список = КлючИЗначение.Ключ;
		Строка.Версия = СтрПолучитьСтроку(КлючИЗначение.Значение, 1)
			+ Символы.ПС + СтрПолучитьСтроку(КлючИЗначение.Значение, 2);
		Строка.ВидИзменения = 1;
	КонецЦикла;
	
	Таблица.Свернуть("Список, Версия", "ВидИзменения");
	Списки = Новый Массив;
	НенайденныеСписки = Новый Массив;
	
	Для Каждого Строка Из Таблица Цикл
		Если Строка.ВидИзменения = 0 Тогда
			Продолжить;
		КонецЕсли;
		ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Строка.Список);
		Если ОбъектМетаданных = Неопределено Тогда
			НенайденныеСписки.Добавить(Строка.Список);
			Продолжить;
		КонецЕсли;
		ПолноеИмя = ОбъектМетаданных.ПолноеИмя();
		Если Списки.Найти(ПолноеИмя) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Списки.Добавить(ПолноеИмя);
	КонецЦикла;
	
	Если НенайденныеСписки.Количество() > 0 Тогда
		НедоступныеСписки = ВсеИдентификаторыСПодобнымиПолнымиИменами(НенайденныеСписки);
	КонецЕсли;
	
	Возврат Списки;
	
КонецФункции

// Для функции СпискиСИзменениемВерсий и для процедуры ДобавитьЗависимыеСписки.
Функция ВсеИдентификаторыСПодобнымиПолнымиИменами(ПолныеИмена)
	
	Запрос = Новый Запрос;
	Запрос.Текст =
	"ВЫБРАТЬ
	|	Идентификаторы.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.ИдентификаторыОбъектовМетаданных КАК Идентификаторы
	|ГДЕ
	|	&УсловияОтбораИОМ
	|
	|ОБЪЕДИНИТЬ ВСЕ
	|
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	Идентификаторы.Идентификатор
	|ИЗ
	|	РегистрСведений.ИдентификаторыОбъектовВерсийРасширений КАК Идентификаторы
	|ГДЕ
	|	&УсловияОтбораИОР";
	
	УсловияОтбораИОМ = Новый Массив;
	УсловияОтбораИОР = Новый Массив;
	
	НомерПараметра = 1;
	Для Каждого ПолноеИмя Из ПолныеИмена Цикл
		ИмяПараметра = "ПолноеИмя" + Формат(НомерПараметра, "ЧГ=");
		Запрос.УстановитьПараметр(ИмяПараметра, ПолноеИмя);
		УсловияОтбораИОМ.Добавить(СтрЗаменить("Идентификаторы.ПолноеИмя ПОДОБНО &ИмяПараметра СПЕЦСИМВОЛ ""~""", 
			"ИмяПараметра", ИмяПараметра)); // @query-part-1
		УсловияОтбораИОР.Добавить(СтрЗаменить("Идентификаторы.ПолноеИмяОбъекта ПОДОБНО &ИмяПараметра СПЕЦСИМВОЛ ""~""",
			"ИмяПараметра", ИмяПараметра)); // @query-part-1
		НомерПараметра = НомерПараметра + 1;
	КонецЦикла;
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловияОтбораИОМ",
		СтрСоединить(УсловияОтбораИОМ, Символы.ПС + "	ИЛИ ")); // @query-part-1
	
	Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловияОтбораИОР",
		СтрСоединить(УсловияОтбораИОР, Символы.ПС + "	ИЛИ ")); // @query-part-1
	
	Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Ссылка");
	
КонецФункции

// Для процедуры ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//   Строка
//
Функция ВерсияСтруктурыКэша()
	
	// Число нужно увеличивать при внесении изменений в состав параметров кэша
	// (в том числе при изменении версии шаблонов).
	Возврат "25" + ВерсияПеревода();
	
КонецФункции

Функция ВерсияПеревода()
	
	Если ВариантВстроенногоЯзыкаРусский() Тогда
		Возврат "";
	Иначе
		Возврат "/" + Метаданные.Версия;
	КонецЕсли;
	
КонецФункции

// Для процедур УстановкаПараметровСеанса, УточнитьВерсииШаблоновОграниченияДоступа и
// для функции СтруктураХранимыхПараметровШаблонов.
//
// Возвращаемое значение:
//   Строка
//
Функция ВерсииШаблоновОграниченияДоступа()
	
	Возврат
	",ДляОбъекта9,
	|,ДляРегистра9,
	|,ПоЗначениям18,
	|,ПоЗначениямРасширенный18,
	|,ПоЗначениямИНаборамРасширенный18,
	|,ПоНаборамЗначений18,";
	
КонецФункции

// Для процедур УстановитьВерсиюПараметров, ЗаполнитьПараметрыДляШаблонов и
// функции НоваяСтруктураХранимыхВерсийПараметровШаблонов.
// 
//
// Возвращаемое значение:
//   Строка
//
Функция ВерсияСтруктурыВерсийПараметровШаблонов()
	
	// Число нужно увеличивать только при внесении изменений
	// в состав параметра ВерсииПараметровШаблонов.
	Возврат "1";
	
КонецФункции

// Для функции ДействующиеПараметрыОграниченияДоступа.
Процедура ОбновитьИдентификаторыТранзакции(ИдентификаторТранзакции)
	
	Кэш = КэшПараметровОграничения();
	
	Если Не ТранзакцияАктивна() И Кэш.ИдентификаторыТранзакции.Количество() = 0
	 Или    ТранзакцияАктивна() И ИдентификаторТранзакции = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если ИдентификаторТранзакции <> Неопределено И ТранзакцияАктивна() Тогда
		Кэш.ИдентификаторыТранзакции.Вставить(ИдентификаторТранзакции, Истина);
	Иначе
		Кэш.ИдентификаторыТранзакции.Очистить();
	КонецЕсли;
	
КонецПроцедуры

// Для функции ДействующиеПараметрыОграниченияДоступа.
Процедура УстановитьВерсиюПараметров(ВерсияПараметров, ИдентификаторТранзакции, ОбщийКонтекст,
			УстановкаПараметровСеансаДляШаблонов = Ложь, ПовторныйВызов = Ложь)
	
	Если Не ПовторныйВызов Тогда
		Если УстановкаПараметровСеансаДляШаблонов Тогда
			СброситьКэшПараметровОграничения();
		Иначе
			ОбновитьПовторноИспользуемыеЗначения();
		КонецЕсли;
	КонецЕсли;
	
	Если ИдентификаторТранзакции <> Неопределено И ТранзакцияАктивна() Тогда
		Кэш = КэшПараметровОграничения();
		Кэш.ИдентификаторыТранзакции.Вставить(ИдентификаторТранзакции, Истина);
	КонецЕсли;
	
	Если ТипЗнч(ВерсияПараметров.ДляЗаписиОбъектовИПроверкиПрав) = Тип("ХранилищеЗначения") Тогда
		ДляЗаписиОбъектовИПроверкиПрав = СтруктураХранимыхПараметровЗаписи(
			ЗначениеИзХранилища(ВерсияПараметров.ДляЗаписиОбъектовИПроверкиПрав));
		
		Если ДляЗаписиОбъектовИПроверкиПрав.ВерсияСтруктурыКэша <> ВерсияСтруктурыКэша() Тогда
			ВерсияПараметров = НоваяВерсияПараметровОграниченияДоступа(ОбщийКонтекст);
			УстановитьВерсиюПараметров(ВерсияПараметров, ИдентификаторТранзакции, ОбщийКонтекст,
				УстановкаПараметровСеансаДляШаблонов, Истина);
			Возврат;
		КонецЕсли;
	Иначе
		ДляЗаписиОбъектовИПроверкиПрав = Неопределено;
	КонецЕсли;
	
	Если ТипЗнч(ВерсияПараметров.ВерсииПараметровШаблонов) = Тип("ХранилищеЗначения") Тогда
		ВерсииПараметровШаблонов = СтруктураХранимыхВерсийПараметровШаблонов(
			ЗначениеИзХранилища(ВерсияПараметров.ВерсииПараметровШаблонов));
		
		Если ВерсииПараметровШаблонов.ВерсияСтруктурыВерсий <> ВерсияСтруктурыВерсийПараметровШаблонов() Тогда
			ВерсияПараметров = НоваяВерсияПараметровОграниченияДоступа(ОбщийКонтекст);
			УстановитьВерсиюПараметров(ВерсияПараметров, ИдентификаторТранзакции, ОбщийКонтекст,
				УстановкаПараметровСеансаДляШаблонов, Истина);
			Возврат;
		КонецЕсли;
	Иначе
		ВерсииПараметровШаблонов = Неопределено;
	КонецЕсли;
	
	Если ТипЗнч(ВерсияПараметров.ДляОтчетаПоПравамДоступа) = Тип("ХранилищеЗначения") Тогда
		ДляОтчетаПоПравамДоступа = СтруктураХранимыхПараметровОтчета(
			ЗначениеИзХранилища(ВерсияПараметров.ДляОтчетаПоПравамДоступа));
		
		Если ДляОтчетаПоПравамДоступа.ВерсияСтруктурыКэша <> ВерсияСтруктурыКэша() Тогда
			ВерсияПараметров = НоваяВерсияПараметровОграниченияДоступа(ОбщийКонтекст);
			УстановитьВерсиюПараметров(ВерсияПараметров, ИдентификаторТранзакции, ОбщийКонтекст,
				УстановкаПараметровСеансаДляШаблонов, Истина);
			Возврат;
		КонецЕсли;
		Кэш = КэшПараметровОграничения();
		Кэш.ВидыОграниченийПравДляПользователей
			= ДляОтчетаПоПравамДоступа.ВидыОграниченийПравДляПользователей;
		Кэш.ВидыОграниченийПравДляВнешнихПользователей
			= ДляОтчетаПоПравамДоступа.ВидыОграниченийПравДляВнешнихПользователей;
	КонецЕсли;
	
	Если УстановкаПараметровСеансаДляШаблонов
	 Или ПараметрыСеанса.ПараметрыОграниченияДоступа.Свойство("ПараметрыСеансаДляШаблоновУстановлены") Тогда
		
		ХранилищеДляШаблоновВСеансах = ?(Пользователи.ЭтоСеансВнешнегоПользователя(),
			ВерсияПараметров.ДляШаблоновВСеансахВнешнихПользователей,
			ВерсияПараметров.ДляШаблоновВСеансахПользователей);
		
		Если ТипЗнч(ХранилищеДляШаблоновВСеансах) = Тип("ХранилищеЗначения") Тогда
			ДляШаблоновОграниченияДоступа = СтруктураХранимыхПараметровШаблонов(
				ЗначениеИзХранилища(ХранилищеДляШаблоновВСеансах));
			
			Если ДляШаблоновОграниченияДоступа.ВерсияСтруктурыКэша <> ВерсияСтруктурыКэша() Тогда
				ВерсияПараметров = НоваяВерсияПараметровОграниченияДоступа(ОбщийКонтекст);
				УстановитьВерсиюПараметров(ВерсияПараметров, ИдентификаторТранзакции, ОбщийКонтекст,
					УстановкаПараметровСеансаДляШаблонов, Истина);
				Возврат;
			КонецЕсли;
		Иначе
			ДляШаблоновОграниченияДоступа = НоваяСтруктураХранимыхПараметровШаблонов();
		КонецЕсли;
		
		ВерсииШаблонов = СтрСоединить(СтрРазделить(
			ДляШаблоновОграниченияДоступа.ВерсииШаблонов, Символы.ПС + Символы.ВК, Ложь), Символы.ПС);
		УточнитьВерсииШаблоновОграниченияДоступа(ВерсииШаблонов);
		
		ПараметрыШаблонов = Новый Структура(СтруктураПараметровШаблонов(
			ДляШаблоновОграниченияДоступа.ПараметрыШаблонов));
		ПараметрыШаблонов.Вставить("ВерсииШаблоновОграниченияДоступа", ВерсииШаблонов);
		
		ОбновитьПараметрыСеансаДляШаблонов = УстановкаПараметровСеансаДляШаблонов;
		Если Не УстановкаПараметровСеансаДляШаблонов
		   И ПараметрыСеанса.ПараметрыОграниченияДоступа.Свойство("ПараметрыСеансаДляШаблоновУстановлены") Тогда
			
			Для Каждого КлючИЗначение Из ПараметрыШаблонов Цикл
				Если ПараметрыСеанса[КлючИЗначение.Ключ] <> КлючИЗначение.Значение Тогда
					ОбновитьПараметрыСеансаДляШаблонов = Истина;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Если ОбновитьПараметрыСеансаДляШаблонов Тогда
			ПередИзменениемПараметровСеансаДляШаблонов(ПараметрыШаблонов, УстановкаПараметровСеансаДляШаблонов);
			ЗаполнитьЗначенияСвойств(ПараметрыСеанса, ПараметрыШаблонов);
		КонецЕсли;
	КонецЕсли;
	
	ОбновитьРазрешенныеНаборыВПараметрахСеанса(УстановкаПараметровСеансаДляШаблонов, Истина);
	
	Параметры = СеансовыеПараметрыОграниченияДоступа(ВерсияПараметров,
		ДляЗаписиОбъектовИПроверкиПрав, ВерсииПараметровШаблонов, УстановкаПараметровСеансаДляШаблонов);
	ПараметрыСеанса.ПараметрыОграниченияДоступа = Параметры;
	
КонецПроцедуры

// Для процедуры УстановитьВерсиюПараметров.
//
// Возвращаемое значение:
//   ФиксированнаяСтруктура:
//     * Версия       - Строка
//     * ХешСумма     - Строка
//     * ДатаСоздания - Дата
//     * Параметры    - см. СтруктураХранимыхПараметровЗаписи
//
Функция СеансовыеПараметрыОграниченияДоступа(ВерсияПараметров, ДляЗаписиОбъектовИПроверкиПрав,
			ВерсииПараметровШаблонов, УстановкаПараметровСеансаДляШаблонов)
	
	ПараметрыОграничения = Новый Структура;
	ПараметрыОграничения.Вставить("Версия",       ВерсияПараметров.Версия);
	ПараметрыОграничения.Вставить("ХешСумма",     ВерсияПараметров.ХешСумма);
	ПараметрыОграничения.Вставить("ДатаСоздания", ВерсияПараметров.ДатаСоздания);
	
	Если ДляЗаписиОбъектовИПроверкиПрав = Неопределено Тогда
		Параметры = Неопределено;
	Иначе
		ПрочитанныеПараметры = Новый Структура(ДляЗаписиОбъектовИПроверкиПрав);
		ДополнительныйКонтекст = Новый Структура(ПрочитанныеПараметры.ДополнительныйКонтекст);
		ДляПользователей = Новый Структура(ДополнительныйКонтекст.ДляПользователей);
		ДляПользователей.ОсновныеВариантыДоступа = Новый ФиксированноеСоответствие(
			ВерсииПараметровШаблонов.ДляПользователей.ОсновныеВариантыДоступа);
		ДополнительныйКонтекст.ДляПользователей = Новый ФиксированнаяСтруктура(ДляПользователей);
		ДляВнешнихПользователей = Новый Структура(ДополнительныйКонтекст.ДляВнешнихПользователей);
		ДляВнешнихПользователей.ОсновныеВариантыДоступа = Новый ФиксированноеСоответствие(
			ВерсииПараметровШаблонов.ДляВнешнихПользователей.ОсновныеВариантыДоступа);
		ДополнительныйКонтекст.ДляВнешнихПользователей = Новый ФиксированнаяСтруктура(ДляВнешнихПользователей);
		ПрочитанныеПараметры.ДополнительныйКонтекст = Новый ФиксированнаяСтруктура(ДополнительныйКонтекст);
		Параметры = Новый ФиксированнаяСтруктура(ПрочитанныеПараметры);
	КонецЕсли;
	
	ПараметрыОграничения.Вставить("Параметры", Параметры);
	
	Если УстановкаПараметровСеансаДляШаблонов
	 Или ПараметрыСеанса.ПараметрыОграниченияДоступа.Свойство("ПараметрыСеансаДляШаблоновУстановлены") Тогда
		ПараметрыОграничения.Вставить("ПараметрыСеансаДляШаблоновУстановлены");
	КонецЕсли;
	
	Если ПараметрыСеанса.ПараметрыОграниченияДоступа.Свойство("ЗаписьПараметровОграниченияДоступаВТекущемСеансе") Тогда
		ПараметрыОграничения.Вставить("ЗаписьПараметровОграниченияДоступаВТекущемСеансе",
			ПараметрыСеанса.ПараметрыОграниченияДоступа.ЗаписьПараметровОграниченияДоступаВТекущемСеансе);
	КонецЕсли;
	
	Возврат Новый ФиксированнаяСтруктура(ПараметрыОграничения);
	
КонецФункции

// Для процедуры УстановитьВерсиюПараметров.
Процедура УточнитьВерсииШаблоновОграниченияДоступа(ВерсииШаблонов)
	
	Если ВерсииШаблонов = ВерсииШаблоновОграниченияДоступа() Тогда
		Возврат;
	КонецЕсли;
	
	ВерсииШаблонов = ВерсииШаблоновОграниченияДоступа() + "
	|,ТребуетсяПерезапуститьСеанс,";
	
КонецПроцедуры

// Для процедуры УстановитьВерсиюПараметров, ПроверитьДоступКОбъекту,
// ПроверитьДоступКНаборуЗаписей и функции ДоступРазрешен.
//
Процедура ОбновитьРазрешенныеНаборыВПараметрахСеанса(УстановкаПараметровСеансаДляШаблонов = Ложь,
			УстановкаПараметровОграниченияДоступа = Ложь)
	
	Если Не УстановкаПараметровСеансаДляШаблонов
	   И Не ПараметрыСеанса.ПараметрыОграниченияДоступа.Свойство("ПараметрыСеансаДляШаблоновУстановлены") Тогда
		Возврат;
	КонецЕсли;
	
	ПоследняяПроверка = УправлениеДоступомСлужебныйПовтИсп.ПоследняяПроверкаВерсииРазрешенныхНаборов();
	
	Если Не УстановкаПараметровСеансаДляШаблонов
	   И Не УстановкаПараметровОграниченияДоступа
	   И ПоследняяПроверка.Дата + 5 >= ТекущаяДатаСеанса() Тогда
		Возврат;
	КонецЕсли;
	
	ПоследняяПроверка.Дата = ТекущаяДатаСеанса();
	
	Состав = РазрешенныеНаборыПараметровЗапроса();
	
	Если Не УстановкаПараметровСеансаДляШаблонов
	   И ПараметрыСеанса.РазрешенныйПользователь            = Состав.РазрешенныйПользователь
	   И ПараметрыСеанса.РазрешенныйНаборГруппДоступа       = Состав.РазрешенныйНаборГруппДоступа
	   И ПараметрыСеанса.РазрешенныйНаборГруппПользователей = Состав.РазрешенныйНаборГруппПользователей
	   И ПараметрыСеанса.РазрешенныйПустойНаборГруппДоступа = Состав.РазрешенныйПустойНаборГруппДоступа Тогда
		
		Возврат;
	КонецЕсли;
	
	Состав.Вставить("ОбщиеПараметрыШаблоновОграниченияДоступа", "");
	УчестьНастройкиПравПриПостроенииПланаВыполненияЗапроса(Состав);
	
	ПередИзменениемПараметровСеансаДляШаблонов(Состав, УстановкаПараметровСеансаДляШаблонов);
	ЗаполнитьЗначенияСвойств(ПараметрыСеанса, Состав);
	
КонецПроцедуры

// Для процедуры ОбновитьРазрешенныеНаборыВПараметрахСеанса и
// УстановитьРазрешенныеНаборыВПараметрыЗапроса.
//
// Параметры:
//  Пользователь   - СправочникСсылка.Пользователи
//                 - СправочникСсылка.ВнешниеПользователи
//                 - Неопределено - текущий пользователь.
//
// Возвращаемое значение:
//  Структура:
//   * РазрешенныйПользователь            - СправочникСсылка.НаборыГруппДоступа
//   * РазрешенныйНаборГруппДоступа       - СправочникСсылка.НаборыГруппДоступа
//   * РазрешенныйНаборГруппПользователей - СправочникСсылка.НаборыГруппДоступа
//   * РазрешенныйПустойНаборГруппДоступа - СправочникСсылка.НаборыГруппДоступа
//
Функция РазрешенныеНаборыПараметровЗапроса(Знач Пользователь = Неопределено)
	
	ПустойНаборГруппДоступа = Справочники.НаборыГруппДоступа.ПустаяСсылка();
	
	Состав = Новый Структура;
	Состав.Вставить("РазрешенныйПользователь",            ПустойНаборГруппДоступа);
	Состав.Вставить("РазрешенныйНаборГруппДоступа",       ПустойНаборГруппДоступа);
	Состав.Вставить("РазрешенныйНаборГруппПользователей", ПустойНаборГруппДоступа);
	Состав.Вставить("РазрешенныйПустойНаборГруппДоступа",
		УправлениеДоступомСлужебныйПовтИсп.РазрешенныйПустойНаборГруппДоступа());
	
	Если Пользователи.ЭтоПолноправныйПользователь(Пользователь,, Ложь) Тогда
		Возврат Состав;
	КонецЕсли;
	
	Если Пользователь = Неопределено Тогда
		Пользователь = Пользователи.АвторизованныйПользователь();
	КонецЕсли;
	
	Состав.РазрешенныйПользователь = Справочники.НаборыГруппДоступа.ПолучитьСсылку(
		Пользователь.УникальныйИдентификатор());
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("РазрешенныйПользователь", Состав.РазрешенныйПользователь);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	НаборыГруппДоступа.РазрешенныйНаборГруппДоступа КАК РазрешенныйНаборГруппДоступа,
	|	НаборыГруппДоступа.РазрешенныйНаборГруппПользователей КАК РазрешенныйНаборГруппПользователей
	|ИЗ
	|	Справочник.НаборыГруппДоступа КАК НаборыГруппДоступа
	|ГДЕ
	|	НаборыГруппДоступа.Ссылка = &РазрешенныйПользователь";
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		Состав.РазрешенныйНаборГруппДоступа       = Выборка.РазрешенныйНаборГруппДоступа;
		Состав.РазрешенныйНаборГруппПользователей = Выборка.РазрешенныйНаборГруппПользователей;
	КонецЕсли;
	
	Возврат Состав;
	
КонецФункции

// Для процедуры ОбновитьРазрешенныеНаборыВПараметрахСеанса.
Процедура УчестьНастройкиПравПриПостроенииПланаВыполненияЗапроса(Состав)
	
	Если Не УправлениеДоступомСлужебныйПовтИсп.ТребуетсяУточнениеПланаЗапроса() Тогда
		Возврат;
	КонецЕсли;
	
	Состав.ОбщиеПараметрыШаблоновОграниченияДоступа = ";УточнитьДляВсех;"
		+ ОписаниеХешСуммыНастроекПрав(Состав.РазрешенныйНаборГруппДоступа)
		+ ОписаниеХешСуммыНастроекПрав(Состав.РазрешенныйПользователь, "^");
	
КонецПроцедуры

// Для процедуры УчестьНастройкиПравПриПостроенииПланаВыполненияЗапроса.
Функция ОписаниеХешСуммыНастроекПрав(НаборГруппДоступа, Символ = "")
	
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.CRC32);
	ХешированиеДанных.Добавить(ПолучитьДвоичныеДанныеИзHexСтроки(СтрЗаменить(
		НаборГруппДоступа.УникальныйИдентификатор(), "-", "")));
	Остаток = ХешированиеДанных.ХешСумма;
	
	Результат = "";
	Для Счетчик = 1 По 32 Цикл
		Целое = Цел(Остаток / 2);
		Результат = ?(Остаток - Целое * 2 = 0, "", XMLСтрока(Счетчик) + Символ + ";") + Результат;
		Остаток = Целое;
	КонецЦикла;
	
	Возврат Символы.ПС + ";" + Результат;
	
КонецФункции

// Для функции ДоступРазрешен и процедур ПроверитьДоступКОбъекту, ПроверитьДоступКНаборуЗаписей.
Процедура УстановитьРазрешенныеНаборыВПараметрыЗапроса(Запрос, Знач Пользователь = Неопределено)
	
	Если Пользователь = Неопределено Тогда
		Параметры = ПараметрыСеанса;
		Пользователь = Пользователи.АвторизованныйПользователь();
	Иначе
		Параметры = РазрешенныеНаборыПараметровЗапроса(Пользователь);
	КонецЕсли;
	
	Запрос.УстановитьПараметр("РазрешенныйНаборГруппДоступа",       Параметры.РазрешенныйНаборГруппДоступа);
	Запрос.УстановитьПараметр("РазрешенныйПустойНаборГруппДоступа", Параметры.РазрешенныйПустойНаборГруппДоступа);
	Запрос.УстановитьПараметр("РазрешенныйНаборГруппПользователей", Параметры.РазрешенныйНаборГруппПользователей);
	Запрос.УстановитьПараметр("РазрешенныйПользователь",            Параметры.РазрешенныйПользователь);
	Запрос.УстановитьПараметр("АвторизованныйПользователь",         Пользователь);
	
КонецПроцедуры

// Для функции НоваяВерсияПараметровОграниченияДоступа.
//
// Параметры:
//  ОбщийКонтекст            - см. ОбщийКонтекстРасчетаПараметровОграничения
//  ВерсииОграниченийСписков - Соответствие
//
// Возвращаемое значение:
//  Структура:
//    * ДатаСоздания                            - Дата
//    * ДляЗаписиОбъектовИПроверкиПрав          - ХранилищеЗначения - содержит тип НоваяСтруктураХранимыхПараметровЗаписи
//    * ДляОтчетаПоПравамДоступа                - ХранилищеЗначения - содержит тип НоваяСтруктураХранимыхПараметровОтчета
//    * ХешСуммаПостоянныхПараметров            - Строка
//    * ДляШаблоновВСеансахПользователей        - ХранилищеЗначения - содержит тип НоваяСтруктураХранимыхПараметровШаблонов
//    * ДляШаблоновВСеансахВнешнихПользователей - ХранилищеЗначения - содержит тип НоваяСтруктураХранимыхПараметровШаблонов
//    * ВерсииПараметровШаблонов                - ХранилищеЗначения - содержит тип НоваяСтруктураХранимыхВерсийПараметровШаблонов
//    * ХешСуммаПараметровШаблонов              - Строка - заполняется при записи после обновления
//         параметра ВерсииПараметровШаблонов и заполнения свойства СпискиСОграничениемПоПолям
//         параметров ДляШаблоновВСеансахПользователей, ДляШаблоновВСеансахВнешнихПользователей.
//    * ХешСумма                                - Строка - заполняется при записи,
//         вычисляется из хеш-сумм ХешСуммаПостоянныхПараметров и ХешСуммаПараметровШаблонов.
//
Функция ХранимыеПараметрыОграниченияДоступа(ОбщийКонтекст, ВерсииОграниченийСписков = Неопределено)
	
	ДатаСоздания = ТекущаяДатаСеанса();
	
	Если ОбщийКонтекст = Неопределено Тогда
		ОбновитьПовторноИспользуемыеЗначения();
		ОбщийКонтекст = ОбщийКонтекстРасчетаПараметровОграничения();
		
	ИначеЕсли ОбщийКонтекст.Свойство("СпециальноеПолноеИмя") Тогда
		СпециальноеПолноеИмя           = ОбщийКонтекст.СпециальноеПолноеИмя;
		СпециальноеОписаниеОграничения = ОбщийКонтекст.СпециальноеОписаниеОграничения;
	КонецЕсли;
	
	ДополнительныйКонтекстДляПользователей        = НовыйДополнительныйКонтекст();
	ДополнительныйКонтекстДляВнешнихПользователей = НовыйДополнительныйКонтекст();
	
	ПолныеИменаСписков = Новый Массив;
	Для Каждого ОписаниеСписка Из ОбщийКонтекст.СпискиСОграничением Цикл
		ПолноеИмя = ОписаниеСписка.Ключ;
		ПолныеИменаСписков.Добавить(ПолноеИмя);
		Если ПолноеИмя = СпециальноеПолноеИмя Тогда
			ОписаниеОграничения = СпециальноеОписаниеОграничения;
		Иначе
			ОписаниеОграничения = ОписаниеОграниченияДанных(ОбщийКонтекст, ПолноеИмя);
		КонецЕсли;
		
		ДобавитьДополнительныйКонтекст(ПолноеИмя,
			ДополнительныйКонтекстДляПользователей, ОписаниеОграничения, Ложь);
		
		ДобавитьДополнительныйКонтекст(ПолноеИмя,
			ДополнительныйКонтекстДляВнешнихПользователей, ОписаниеОграничения, Истина);
	КонецЦикла;
	
	СпискиСДатой = Новый Соответствие;
	ИдентификаторыСписков = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(ПолныеИменаСписков);
	
	ВидыОграниченийПравДляПользователей        = Новый Соответствие;
	ВидыОграниченийПравДляВнешнихПользователей = Новый Соответствие;
	
	Если ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
		СтрокиВерсийСписков = НовыеСтрокиВерсийСписков();
		СтрокиВерсииХранимыхПараметров = Новый Структура;
		СтрокиВерсииХранимыхПараметров.Вставить("ВсеВерсииСтрокой", "");
		СтрокиВерсииХранимыхПараметров.Вставить("ВидыОграниченийПравДляПользователейСтрокой", "");
		СтрокиВерсииХранимыхПараметров.Вставить("ВидыОграниченийПравДляВнешнихПользователейСтрокой", "");
		СтрокиВерсииХранимыхПараметров.Вставить("СтрокиВерсийСписков", СтрокиВерсийСписков);
		ОбщийКонтекст.СтрокиВерсииХранимыхПараметров = СтрокиВерсииХранимыхПараметров;
	КонецЕсли;
	
	// Заполнение для пользователей.
	КонтекстДляПользователей = Новый Структура;
	КонтекстДляПользователей.Вставить("ДляВнешнихПользователей",  Ложь);
	КонтекстДляПользователей.Вставить("ДатаСоздания",             ДатаСоздания);
	КонтекстДляПользователей.Вставить("ОбщийКонтекст",            ОбщийКонтекст);
	КонтекстДляПользователей.Вставить("ДополнительныйКонтекст",   ДополнительныйКонтекстДляПользователей);
	КонтекстДляПользователей.Вставить("ВедущиеСписки",            Новый Соответствие);
	КонтекстДляПользователей.Вставить("ВерсииОграниченийСписков", Новый Соответствие);
	КонтекстДляПользователей.Вставить("ПараметрыШаблонов",        НоваяСтруктураПараметровШаблонов());
	КонтекстДляПользователей.Вставить("ВерсииПараметровШаблонов", НовыеВерсииПараметровШаблонов());
	КонтекстДляПользователей.Вставить("СпискиСДатой",             СпискиСДатой);
	КонтекстДляПользователей.Вставить("ИдентификаторыСписков",    ИдентификаторыСписков);
	КонтекстДляПользователей.Вставить("ВсеВидыОграниченийПрав",   ВидыОграниченийПравДляПользователей);
	КонтекстДляПользователей.Вставить("ВедущиеРоли",              Новый Соответствие);
	
	ДобавитьХранимыеПараметрыОграниченияДляВидаПользователей(КонтекстДляПользователей);
	
	// Заполнение для внешних пользователей.
	КонтекстДляВнешнихПользователей = Новый Структура;
	КонтекстДляВнешнихПользователей.Вставить("ДляВнешнихПользователей",  Истина);
	КонтекстДляВнешнихПользователей.Вставить("ДатаСоздания",             ДатаСоздания);
	КонтекстДляВнешнихПользователей.Вставить("ОбщийКонтекст",            ОбщийКонтекст);
	КонтекстДляВнешнихПользователей.Вставить("ДополнительныйКонтекст",   ДополнительныйКонтекстДляВнешнихПользователей);
	КонтекстДляВнешнихПользователей.Вставить("ВедущиеСписки",            Новый Соответствие);
	КонтекстДляВнешнихПользователей.Вставить("ВерсииОграниченийСписков", Новый Соответствие);
	КонтекстДляВнешнихПользователей.Вставить("ПараметрыШаблонов",        НоваяСтруктураПараметровШаблонов());
	КонтекстДляВнешнихПользователей.Вставить("ВерсииПараметровШаблонов", НовыеВерсииПараметровШаблонов());
	КонтекстДляВнешнихПользователей.Вставить("СпискиСДатой",             СпискиСДатой);
	КонтекстДляВнешнихПользователей.Вставить("ИдентификаторыСписков",    ИдентификаторыСписков);
	КонтекстДляВнешнихПользователей.Вставить("ВсеВидыОграниченийПрав",   ВидыОграниченийПравДляВнешнихПользователей);
	КонтекстДляВнешнихПользователей.Вставить("ВедущиеРоли",              Новый Соответствие);
	
	ДобавитьХранимыеПараметрыОграниченияДляВидаПользователей(КонтекстДляВнешнихПользователей);
	
	// Заполнение общей и отдельной частей ведущих списков для пользователей и внешних пользователей.
	ВедущиеСписки = Новый Соответствие;
	Для Каждого ОписаниеВедущихСписков Из КонтекстДляПользователей.ВедущиеСписки Цикл
		ДобавитьВедущиеСписки(ВедущиеСписки,
			"ДляПользователей", ОписаниеВедущихСписков.Ключ, ОписаниеВедущихСписков.Значение);
	КонецЦикла;
	Для Каждого ОписаниеВедущихСписков Из КонтекстДляВнешнихПользователей.ВедущиеСписки Цикл
		ДобавитьВедущиеСписки(ВедущиеСписки,
			"ДляВнешнихПользователей", ОписаниеВедущихСписков.Ключ, ОписаниеВедущихСписков.Значение);
	КонецЦикла;
	Свойства = ВедущийСписокПоЗначениямПолей();
	Для Каждого ВедущийСписок Из ВедущиеСписки Цикл
		Свойства = ВедущийСписок.Значение;
		Если Свойства.ПоЗначениямПолей <> Неопределено Тогда
			ПоЗначениямПолей = Свойства.ПоЗначениямПолей;
			Если ПоЗначениямПолей.ЭтоСсылочныйТип Тогда
				ТабличныеЧасти = Новый Массив;
				Для Каждого ОписаниеТабличнойЧасти Из ПоЗначениямПолей.ТабличныеЧасти Цикл
					ТабличныеЧасти.Добавить(ОписаниеТабличнойЧасти.Значение);
				КонецЦикла;
				ПоЗначениямПолей.ТабличныеЧасти = ТабличныеЧасти;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	// Заполнение ведущих ролей для пользователей и внешних пользователей.
	ВедущиеРоли = Новый Соответствие;
	ДобавитьВедущиеРоли(ВедущиеРоли, "ДляПользователей", КонтекстДляПользователей);
	ДобавитьВедущиеРоли(ВедущиеРоли, "ДляВнешнихПользователей", КонтекстДляВнешнихПользователей);
	
	// Расчет версий ограничения доступа.
	ВерсииОграниченийСписков = Новый Соответствие;
	Версии = Новый СписокЗначений;
	Версии.Добавить(ВерсияСтруктурыКэша());
	Для Каждого ОписаниеВерсии Из КонтекстДляПользователей.ВерсииОграниченийСписков Цикл
		ОбщаяВерсия = ОбщаяВерсия(ОбщийКонтекст, ОписаниеВерсии.Ключ, ОписаниеВерсии.Значение,
			КонтекстДляВнешнихПользователей.ВерсииОграниченийСписков.Получить(ОписаниеВерсии.Ключ));
		ВерсииОграниченийСписков.Вставить(ОписаниеВерсии.Ключ, ОбщаяВерсия);
		Версии.Добавить(ОбщаяВерсия, ПолучитьHexСтрокуИзДвоичныхДанных(
			ПолучитьДвоичныеДанныеИзСтроки(Строка(ОбщаяВерсия))));
	КонецЦикла;
	Версии.СортироватьПоПредставлению();
	ВерсииСтрокой = СтрСоединить(Версии.ВыгрузитьЗначения(), Символы.ПС);
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.SHA256);
	ХешированиеДанных.Добавить(ВерсииСтрокой);
	Если ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
		ОбщийКонтекст.СтрокиВерсииХранимыхПараметров.ВсеВерсииСтрокой = ВерсииСтрокой;
	КонецЕсли;
	
	// Подготовка дополнительного контекста для расчета параметров ограничения отдельного списка.
	ДляПользователей = НовыйХранимыйДополнительныйКонтекст();
	ЗаполнитьЗначенияСвойств(ДляПользователей, КонтекстДляПользователей.ДополнительныйКонтекст);
	ДляВнешнихПользователей = НовыйХранимыйДополнительныйКонтекст();
	ЗаполнитьЗначенияСвойств(ДляВнешнихПользователей, КонтекстДляВнешнихПользователей.ДополнительныйКонтекст);
	ДополнительныйКонтекст = Новый Структура;
	ДополнительныйКонтекст.Вставить("ДляПользователей", ДляПользователей);
	ДополнительныйКонтекст.Вставить("ДляВнешнихПользователей", ДляВнешнихПользователей);
	
	ПараметрыЗаписи = НоваяСтруктураХранимыхПараметровЗаписи();
	ПараметрыЗаписи.ВерсииОграниченийСписков = ВерсииОграниченийСписков;
	ПараметрыЗаписи.ВедущиеСписки            = ВедущиеСписки;
	ПараметрыЗаписи.ДополнительныйКонтекст   = ДополнительныйКонтекст;
	ПараметрыЗаписи.СпискиСДатой             = СпискиСДатой;
	ПараметрыЗаписи.ВедущиеРоли              = ВедущиеРоли;
	ПараметрыЗаписи.ИспользуемыеТипыЗначений = Новый ХранилищеЗначения(
		Новый Структура("ХешСумма", ОбщийКонтекст.ИспользуемыеТипыЗначений.ХешСумма));
	ПараметрыЗаписи.ВнешниеПользователиВключены = ОбщийКонтекст.ВнешниеПользователиВключены;
	ПараметрыЗаписи.ОграничениеДоступаВключено  = ОбщийКонтекст.ОграничениеДоступаВключено;
	
	ХранимыеПараметры = Новый Структура;
	
	ХранимыеПараметрыШаблоновДляПользователей = НоваяСтруктураХранимыхПараметровШаблонов();
	ХранимыеПараметрыШаблоновДляПользователей.ПараметрыШаблонов = КонтекстДляПользователей.ПараметрыШаблонов;
	ХранимыеПараметры.Вставить("ДляШаблоновВСеансахПользователей",
		Новый ХранилищеЗначения(ОбщегоНазначения.ФиксированныеДанные(ХранимыеПараметрыШаблоновДляПользователей)));
	
	ХранимыеПараметрыШаблоновДляВнешнихПользователей = НоваяСтруктураХранимыхПараметровШаблонов();
	ХранимыеПараметрыШаблоновДляВнешнихПользователей.ПараметрыШаблонов = КонтекстДляВнешнихПользователей.ПараметрыШаблонов;
	ХранимыеПараметры.Вставить("ДляШаблоновВСеансахВнешнихПользователей",
		Новый ХранилищеЗначения(ОбщегоНазначения.ФиксированныеДанные(ХранимыеПараметрыШаблоновДляВнешнихПользователей)));
	
	ХранимыеВерсииПараметровШаблонов = НоваяСтруктураХранимыхВерсийПараметровШаблонов();
	ХранимыеВерсииПараметровШаблонов.ДляПользователей.ВерсииПараметровШаблонов =
		КонтекстДляПользователей.ВерсииПараметровШаблонов;
	ХранимыеВерсииПараметровШаблонов.ДляВнешнихПользователей.ВерсииПараметровШаблонов =
		КонтекстДляВнешнихПользователей.ВерсииПараметровШаблонов;
	ХранимыеПараметры.Вставить("ВерсииПараметровШаблонов",
		Новый ХранилищеЗначения(ХранимыеВерсииПараметровШаблонов));
	
	ХранимыеПараметры.Вставить("ДляЗаписиОбъектовИПроверкиПрав",
		Новый ХранилищеЗначения(ОбщегоНазначения.ФиксированныеДанные(ПараметрыЗаписи)));
	
	ХранимыеПараметрыОтчета = НоваяСтруктураХранимыхПараметровОтчета();
	ХранимыеПараметрыОтчета.ВидыОграниченийПравДляПользователей =
		ВидыОграниченийПравСтрокой(ВидыОграниченийПравДляПользователей);
	ХешированиеДанных.Добавить(ХранимыеПараметрыОтчета.ВидыОграниченийПравДляПользователей);
	ХранимыеПараметрыОтчета.ВидыОграниченийПравДляВнешнихПользователей =
		ВидыОграниченийПравСтрокой(ВидыОграниченийПравДляВнешнихПользователей);
	ХешированиеДанных.Добавить(ХранимыеПараметрыОтчета.ВидыОграниченийПравДляВнешнихПользователей);
	Если ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
		ОбщийКонтекст.СтрокиВерсииХранимыхПараметров.ВидыОграниченийПравДляПользователейСтрокой
			= ХранимыеПараметрыОтчета.ВидыОграниченийПравДляПользователей;
		ОбщийКонтекст.СтрокиВерсииХранимыхПараметров.ВидыОграниченийПравДляВнешнихПользователейСтрокой
			= ХранимыеПараметрыОтчета.ВидыОграниченийПравДляВнешнихПользователей;
	КонецЕсли;
	
	ХранимыеПараметры.Вставить("ДляОтчетаПоПравамДоступа",
		Новый ХранилищеЗначения(ОбщегоНазначения.ФиксированныеДанные(ХранимыеПараметрыОтчета)));
	
	ХранимыеПараметры.Вставить("ХешСуммаПостоянныхПараметров", Base64Строка(ХешированиеДанных.ХешСумма));
	ХранимыеПараметры.Вставить("ДатаСоздания", ДатаСоздания);
	
	Если ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
		СтрокиВерсийСписков.Сортировать("Список, ДляВнешнихПользователей");
	КонецЕсли;
	
	Возврат ХранимыеПараметры;
	
КонецФункции

// Возвращаемое значение:
//   ТаблицаЗначений:
//     * Список                  - Строка
//     * ДляВнешнихПользователей - Булево
//     * Версия                  - Строка
//     * СтрокаВерсии            - Строка
//
Функция НовыеСтрокиВерсийСписков()
	
	СтрокиВерсийСписков = Новый ТаблицаЗначений;
	СтрокиВерсийСписков.Колонки.Добавить("Список",                  Новый ОписаниеТипов("Строка"));
	СтрокиВерсийСписков.Колонки.Добавить("ДляВнешнихПользователей", Новый ОписаниеТипов("Булево"));
	СтрокиВерсийСписков.Колонки.Добавить("Версия",                  Новый ОписаниеТипов("Строка"));
	СтрокиВерсийСписков.Колонки.Добавить("СтрокаВерсии",            Новый ОписаниеТипов("Строка"));
	
	Возврат СтрокиВерсийСписков;
	
КонецФункции

// Для функций РассчитанныеПараметрыОграничения и ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//   Структура:
//     * ОписанияОграничений - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - см. СокращенноеОписаниеОграничения
//     * СвойстваОграниченияСписков - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - см. НовыеСвойстваОграниченияСписка
//     * СпискиСОграничениемПоВладельцу - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - значение ПоВладельцу, кроме Неопределено.
//     * СпискиСОтключеннымОграничением - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * СпискиСОтключеннымОграничениемЧтения - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * ОсновныеВариантыДоступа - см. НовыеОсновныеВариантыДоступа
//
Функция НовыйДополнительныйКонтекст()
	
	ДополнительныйКонтекст = Новый Структура;
	ДополнительныйКонтекст.Вставить("ОписанияОграничений",                  Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СвойстваОграниченияСписков",           Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СпискиСОграничениемПоВладельцу",       Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СпискиСОтключеннымОграничением",       Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СпискиСОтключеннымОграничениемЧтения", Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей", Неопределено);
	ДополнительныйКонтекст.Вставить("ОсновныеВариантыДоступа",              Новый Соответствие);
	
	Возврат ДополнительныйКонтекст;
	
КонецФункции

// Для функций РассчитанныеПараметрыОграничения и ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//   Структура:
//     * СвойстваОграниченияСписков - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - см. НовыеСвойстваОграниченияСписка
//     * СпискиСОтключеннымОграничением - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * СпискиСОтключеннымОграничениемЧтения - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * ОсновныеВариантыДоступа - см. НовыеОсновныеВариантыДоступа
//
Функция НовыйХранимыйДополнительныйКонтекст()
	
	ДополнительныйКонтекст = Новый Структура;
	ДополнительныйКонтекст.Вставить("СвойстваОграниченияСписков",           Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СпискиСОтключеннымОграничением",       Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СпискиСОтключеннымОграничениемЧтения", Новый Соответствие);
	ДополнительныйКонтекст.Вставить("СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей", Неопределено);
	ДополнительныйКонтекст.Вставить("ОсновныеВариантыДоступа",              Новый Соответствие);
	
	Возврат ДополнительныйКонтекст;
	
КонецФункции

// Для функций РассчитанныеПараметрыОграничения и ХранимыеПараметрыОграниченияДоступа.
Процедура ДобавитьДополнительныйКонтекст(ПолноеИмя, ДополнительныйКонтекст,
				ОписаниеОграничения, ДляВнешнихПользователей)
	
	Если ДляВнешнихПользователей Тогда
		Текст               = ОписаниеОграничения.ТекстДляВнешнихПользователей;
		ПоВладельцу         = ОписаниеОграничения.ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей;
	Иначе
		Текст               = ОписаниеОграничения.Текст;
		ПоВладельцу         = ОписаниеОграничения.ПоВладельцуБезЗаписиКлючейДоступа;
	КонецЕсли;
	ВМодулеМенеджера = ОписаниеОграничения.ТекстВМодулеМенеджера;
	
	СокращенноеОписаниеОграничения = СокращенноеОписаниеОграничения();
	СокращенноеОписаниеОграничения.Вставить("Текст",               Текст);
	СокращенноеОписаниеОграничения.Вставить("ВМодулеМенеджера",    ВМодулеМенеджера);
	
	ДополнительныйКонтекст.ОписанияОграничений.Вставить(ПолноеИмя, СокращенноеОписаниеОграничения);
	
	Если ПоВладельцу <> Неопределено Тогда
		ДополнительныйКонтекст.СпискиСОграничениемПоВладельцу.Вставить(ПолноеИмя, ПоВладельцу);
	КонецЕсли;
	
КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//     * Текст            - Строка
//     * ВМодулеМенеджера - Булево
//
Функция СокращенноеОписаниеОграничения()
	
	Возврат Новый Структура;
	
КонецФункции

// Для функции ХранимыеПараметрыОграниченияДоступа.
Процедура ДобавитьХранимыеПараметрыОграниченияДляВидаПользователей(Контекст)
	
	// Подготовка параметров с учетом зависимостей только по ключам доступа.
	ТаблицаСвойств = СвойстваСписковДляРасчетаХранимыхПараметров();
	ТаблицаСвойств = Новый ТаблицаЗначений;
	ТаблицаСвойств.Колонки.Добавить("ПолноеИмя", Новый ОписаниеТипов("Строка"));
	ТаблицаСвойств.Колонки.Добавить("Зависимый", Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("Ведущие",   Новый ОписаниеТипов("Массив"));
	ТаблицаСвойств.Колонки.Добавить("Уровень",   Новый ОписаниеТипов("Число"));
	ТаблицаСвойств.Колонки.Добавить("Ведущий",   Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("Зависимые", Новый ОписаниеТипов("Массив"));
	ТаблицаСвойств.Колонки.Добавить("Параметры", Новый ОписаниеТипов("Структура"));
	ТаблицаСвойств.Колонки.Добавить("Обработан", Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("ОграничениеПоВладельцуВозможно",  Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("ОграничениеПоВладельцуВключено",  Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("ОграничениеПоВладельцуОтключено", Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("КлючиДоступаПользователей",       Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа", Новый ОписаниеТипов("Булево"));
	ТаблицаСвойств.Колонки.Добавить("КлючиДоступаПользователейИГруппДоступа",    Новый ОписаниеТипов("Булево"));
	
	СпискиСОграничением = Новый Соответствие(Контекст.ОбщийКонтекст.СпискиСОграничением);
	СпискиСОтключеннымОграничением       = Новый Соответствие;
	СпискиСОтключеннымОграничениемЧтения = Новый Соответствие;
	
	Для Каждого ОписаниеСписка Из СпискиСОграничением Цикл
		ПолноеИмя = ОписаниеСписка.Ключ;
		
		Если ТипЗнч(ОписаниеСписка.Значение) = Тип("Булево") Тогда
			Свойства = ТаблицаСвойств.Добавить();
			Свойства.ПолноеИмя = ПолноеИмя;
			СпискиСОграничением.Вставить(ПолноеИмя, Свойства);
			Параметры = ПараметрыОграниченияДляВидаПользователей(ПолноеИмя, Контекст);
			Свойства.Параметры = Параметры;
		Иначе
			Свойства = ОписаниеСписка.Значение; // См. СвойстваСписковДляРасчетаХранимыхПараметров
			Параметры = Свойства.Параметры;
		КонецЕсли;
		
		Если Параметры.ОграничениеЧтенияОтключено Тогда
			СпискиСОтключеннымОграничениемЧтения.Вставить(ПолноеИмя, Истина);
		КонецЕсли;
		Если Параметры.ОграничениеОтключено Тогда
			СпискиСОтключеннымОграничением.Вставить(ПолноеИмя, Истина);
		КонецЕсли;
		Если Параметры.ЕстьОграничениеПоПользователям Тогда
			Свойства.КлючиДоступаПользователей = Истина;
			УстановитьСвойствоОграничения(ПолноеИмя, "РассчитыватьПраваПользователей", Истина, Контекст);
		КонецЕсли;
		Если Параметры.ПолеВладельца <> Неопределено Тогда
			УстановитьСвойствоОграничения(ПолноеИмя, "ПолеВладельца", Параметры.ПолеВладельца, Контекст);
		КонецЕсли;
		
		ВедущиеСписки = Параметры.ВедущиеСписки;
		Если ВедущиеСписки.ПоКлючамДоступа.Количество() > 0 Тогда
			Свойства.Зависимый = Истина;
			Для Каждого КлючИЗначение Из ВедущиеСписки.ПоКлючамДоступа Цикл
				ВедущийСписок = КлючИЗначение.Ключ;
				Свойства.Ведущие.Добавить(ВедущийСписок);
				СвойстваВедущего = СпискиСОграничением.Получить(ВедущийСписок);
				Если СвойстваВедущего = Неопределено Или ТипЗнч(СвойстваВедущего) = Тип("Булево") Тогда
					СвойстваВедущего = ТаблицаСвойств.Добавить();
					СвойстваВедущего.ПолноеИмя = ВедущийСписок;
					СпискиСОграничением.Вставить(ВедущийСписок, СвойстваВедущего);
					ПараметрыВедущего = ПараметрыОграниченияДляВидаПользователей(ВедущийСписок, Контекст);
					СвойстваВедущего.Параметры = ПараметрыВедущего;
					Если ПараметрыВедущего.ОграничениеЧтенияОтключено Тогда
						СпискиСОтключеннымОграничениемЧтения.Вставить(ВедущийСписок, Истина);
					КонецЕсли;
					Если ПараметрыВедущего.ОграничениеОтключено Тогда
						СпискиСОтключеннымОграничением.Вставить(ВедущийСписок, Истина);
					КонецЕсли;
					Если ПараметрыВедущего.ЕстьОграничениеПоПользователям Тогда
						СвойстваВедущего.КлючиДоступаПользователей = Истина;
						УстановитьСвойствоОграничения(ВедущийСписок,
							"РассчитыватьПраваПользователей", Истина, Контекст);
					КонецЕсли;
					Если ПараметрыВедущего.ПолеВладельца <> Неопределено Тогда
						УстановитьСвойствоОграничения(ВедущийСписок,
							"ПолеВладельца", ПараметрыВедущего.ПолеВладельца, Контекст);
					КонецЕсли;
				КонецЕсли;
				СвойстваВедущего.Ведущий = Истина;
				СвойстваВедущего.Зависимые.Добавить(ПолноеИмя);
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
	МаксимальныйУровень = 0;
	Строки = ТаблицаСвойств.НайтиСтроки(Новый Структура("Зависимый, Ведущий", Ложь, Истина));
	Для Каждого Строка Из Строки Цикл
		УстановитьУровеньЗависимыхСписков(Строка, СпискиСОграничением, Новый Массив, МаксимальныйУровень);
	КонецЦикла;
	// Обработка зависимых списков, ведущих для самих себя (зацикленных на себя).
	Строки = ТаблицаСвойств.НайтиСтроки(Новый Структура("Обработан", Ложь));
	Для Каждого Строка Из Строки Цикл
		УстановитьУровеньЗависимыхСписков(Строка, СпискиСОграничением, Новый Массив, МаксимальныйУровень);
	КонецЦикла;
	
	Строки = ТаблицаСвойств.НайтиСтроки(Новый Структура("Зависимый, Ведущий", Истина, Ложь));
	Для Каждого Строка Из Строки Цикл
		НастроитьОптимизациюПоПолюВладельцу(Строка, СпискиСОграничением, Контекст);
	КонецЦикла;
	
	// Сокращение зависимостей по ключам доступа зависимых объектов.
	СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей = Новый Соответствие;
	Отбор = Новый Структура("ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа", Истина);
	Строки = ТаблицаСвойств.НайтиСтроки(Отбор);
	Для Каждого Строка Из Строки Цикл
		СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей.Вставить(Строка.ПолноеИмя, Истина);
	КонецЦикла;
	ДополнительныйКонтекст = Контекст.ДополнительныйКонтекст;
	ДополнительныйКонтекст.СпискиСОтключеннымОграничениемЧтения = СпискиСОтключеннымОграничениемЧтения;
	ДополнительныйКонтекст.СпискиСОтключеннымОграничением       = СпискиСОтключеннымОграничением;
	ДополнительныйКонтекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей
		= СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей;
	
	Отбор = Новый Структура("Уровень, ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа", 0, Истина);
	Строки = ТаблицаСвойств.НайтиСтроки(Отбор);
	Для Каждого Строка Из Строки Цикл
		Строка.Параметры = ПараметрыОграниченияДляВидаПользователей(Строка.ПолноеИмя, Контекст);
	КонецЦикла;
	
	СвойстваСписковПоУровням = Новый Массив;
	Для Уровень = 1 По МаксимальныйУровень Цикл
		СвойстваСписков = ТаблицаСвойств.НайтиСтроки(Новый Структура("Уровень", Уровень));
		СвойстваСписковПоУровням.Добавить(СвойстваСписков);
	КонецЦикла;
	
	ОбщийКонтекстСпискиСОграничением = Контекст.ОбщийКонтекст.СпискиСОграничением;
	
	Для Каждого СвойстваСписков Из СвойстваСписковПоУровням Цикл
		Для Каждого СвойстваСписка Из СвойстваСписков Цикл
			СвойстваСписка = СвойстваСписка; // См. СвойстваСписковДляРасчетаХранимыхПараметров
			
			ВсеВедущиеСпискиСОграничением = Истина;
			ИзмененоСвойствоРассчитыватьПраваПользователей = Ложь;
			КлючиДоступаПользователей = Ложь;
			КлючиДоступаГруппДоступа = Ложь;
			
			Для Каждого Ведущий Из СвойстваСписка.Ведущие Цикл
				СвойстваВедущего = СпискиСОграничением.Получить(Ведущий);
				Если СвойстваВедущего.КлючиДоступаПользователей Тогда
					КлючиДоступаПользователей = Истина;
				Иначе
					КлючиДоступаГруппДоступа = Истина;
				КонецЕсли;
				Если ОбщийКонтекстСпискиСОграничением.Получить(Ведущий) = Неопределено
				 Или СпискиСОтключеннымОграничением.Получить(Ведущий) <> Неопределено Тогда
					ВсеВедущиеСпискиСОграничением = Ложь;
				КонецЕсли;
			КонецЦикла;
			
			Если КлючиДоступаПользователей Тогда
				Если Не СвойстваСписка.ОграничениеПоВладельцуВключено
				   И Не СвойстваСписка.КлючиДоступаПользователей Тогда
					
					ИзмененоСвойствоРассчитыватьПраваПользователей = Истина;
					УстановитьСвойствоОграничения(СвойстваСписка.ПолноеИмя,
						"РассчитыватьПраваПользователей", Истина, Контекст);
				КонецЕсли;
				СвойстваСписка.КлючиДоступаПользователей = Истина;
				Если КлючиДоступаГруппДоступа Тогда
					СвойстваСписка.КлючиДоступаПользователейИГруппДоступа = Истина;
				КонецЕсли;
			КонецЕсли;
			
			Если СвойстваСписка.ОграничениеПоВладельцуВключено
			   И ВсеВедущиеСпискиСОграничением
			   И Не ИзмененоСвойствоРассчитыватьПраваПользователей
			   И СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей.Получить(СвойстваСписка.ПолноеИмя) = Неопределено
			    Тогда
				Продолжить;
			КонецЕсли;
			
			СвойстваСписка.Параметры =
				ПараметрыОграниченияДляВидаПользователей(СвойстваСписка.ПолноеИмя, Контекст);
		КонецЦикла;
	КонецЦикла;
	
	// Заполнение хранимых свойств списков.
	Для Каждого СвойстваСписка Из ТаблицаСвойств Цикл
		ПолноеИмя = СвойстваСписка.ПолноеИмя;
		
		Если СвойстваСписка.Параметры.ДоступЗапрещен Тогда
			УстановитьСвойствоОграничения(ПолноеИмя, "ДоступЗапрещен", Истина, Контекст);
		КонецЕсли;
		Если ЗначениеЗаполнено(СвойстваСписка.Параметры.ИмяОтдельногоРегистраКлючей) Тогда
			УстановитьСвойствоОграничения(ПолноеИмя,
				"ИмяОтдельногоРегистраКлючей", СвойстваСписка.Параметры.ИмяОтдельногоРегистраКлючей, Контекст);
		КонецЕсли;
		Если ЗначениеЗаполнено(СвойстваСписка.Параметры.ОпорныеПоля) Тогда
			ОпорныеПоля = Новый Структура("Все, ТипыВсех, Используемые");
			ЗаполнитьЗначенияСвойств(ОпорныеПоля, СвойстваСписка.Параметры.ОпорныеПоля);
			УстановитьСвойствоОграничения(ПолноеИмя, "ОпорныеПоля", ОпорныеПоля, Контекст);
		КонецЕсли;
		Если СвойстваСписка.КлючиДоступаПользователейИГруппДоступа Тогда
			УстановитьСвойствоОграничения(ПолноеИмя,
				"ОграничениеВШаблонахЧерезКлючиДоступаПользователейИГруппДоступа", Истина, Контекст);
		ИначеЕсли СвойстваСписка.КлючиДоступаПользователей Тогда
			УстановитьСвойствоОграничения(ПолноеИмя,
				"ОграничениеВШаблонахЧерезКлючиДоступаПользователей", Истина, Контекст);
		КонецЕсли;
		Если СвойстваСписка.Параметры.ОграничениеЧтенияОтключено Тогда
			СпискиСОтключеннымОграничениемЧтения.Вставить(ПолноеИмя, Истина);
		КонецЕсли;
		Если СвойстваСписка.Параметры.ОграничениеОтключено Тогда
			СпискиСОтключеннымОграничением.Вставить(ПолноеИмя, Истина);
		КонецЕсли;
		УстановитьСвойствоОграничения(ПолноеИмя, "ИспользуемыеТипыЗначенийДоступа",
			Новый ХранилищеЗначения(СвойстваСписка.Параметры.ИспользуемыеТипыЗначенийДоступа), Контекст);
		
		Контекст.ВерсииОграниченийСписков.Вставить(ПолноеИмя, СвойстваСписка.Параметры.Версия);
		Если Контекст.ОбщийКонтекст.Свойство("СтрокиВерсииХранимыхПараметров") Тогда
			СтрокиВерсийСписков = Контекст.ОбщийКонтекст.СтрокиВерсииХранимыхПараметров.СтрокиВерсийСписков; // См. НовыеСтрокиВерсийСписков
			НоваяСтрока = СтрокиВерсийСписков.Добавить();
			НоваяСтрока.Список                  = СвойстваСписка.ПолноеИмя;
			НоваяСтрока.ДляВнешнихПользователей = Контекст.ДляВнешнихПользователей;
			НоваяСтрока.Версия                  = СвойстваСписка.Параметры.Версия;
			НоваяСтрока.СтрокаВерсии            = СвойстваСписка.Параметры.Контекст.СтрокаСвойствВерсии;
		КонецЕсли;
		
		ВедущиеСписки = СвойстваСписка.Параметры.ВедущиеСписки;
		
		Если ВедущиеСписки.ПоЗначениямПолей.Количество() > 0
		 Или ВедущиеСписки.ПоКлючамДоступа.Количество() > 0
		 Или ВедущиеСписки.ПоЗначениямСГруппами.Количество() > 0 Тогда
			
			Контекст.ВедущиеСписки.Вставить(ПолноеИмя, ВедущиеСписки);
		КонецЕсли;
		
		НастроитьПараметрыШаблонов(СвойстваСписка, Контекст);
		
		Если СвойстваСписка.Параметры.СписокСДатой Тогда
			Контекст.СпискиСДатой.Вставить(СвойстваСписка.ПолноеИмя, Истина);
		КонецЕсли;
		
		Для Каждого КлючИЗначение Из СвойстваСписка.Параметры.ВсеВидыОграниченийПрав Цикл
			Контекст.ВсеВидыОграниченийПрав.Вставить(СвойстваСписка.ПолноеИмя + "." + КлючИЗначение.Ключ, Истина);
		КонецЦикла;
		
		Если ЗначениеЗаполнено(СвойстваСписка.Параметры.Контекст.ВедущиеРоли) Тогда
			Для Каждого КлючИЗначение Из СвойстваСписка.Параметры.Контекст.ВедущиеРоли Цикл
				ЗависимыеСписки = Контекст.ВедущиеРоли.Получить(КлючИЗначение.Ключ);
				Если ЗависимыеСписки = Неопределено Тогда
					ЗависимыеСписки = Новый Соответствие;
					Контекст.ВедущиеРоли.Вставить(КлючИЗначение.Ключ, ЗависимыеСписки);
				КонецЕсли;
				ЗависимыеСписки.Вставить(СвойстваСписка.ПолноеИмя, Истина);
			КонецЦикла;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//     * ПолноеИмя - Строка
//     * Зависимый - Булево
//     * Ведущие   - Массив
//     * Уровень   - Число
//     * Ведущий   - Булево
//     * Параметры - Структура
//     * Обработан - Булево
//     * ОграничениеПоВладельцуВозможно  - Булево
//     * ОграничениеПоВладельцуВключено  - Булево
//     * ОграничениеПоВладельцуОтключено - Булево
//     * КлючиДоступаПользователей       - Булево
//     * ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа - Булево
//     * КлючиДоступаПользователейИГруппДоступа    - Булево
//
Функция СвойстваСписковДляРасчетаХранимыхПараметров()
	
	Возврат Неопределено;
	
КонецФункции

// Для процедуры ХранимыеПараметрыОграниченияДляВидаПользователей.
//
// Возвращаемое значение:
//   см. ПараметрыОграниченияПоСтруктуреОграничения
//
Функция ПараметрыОграниченияДляВидаПользователей(ПолноеИмя, Контекст)
	
	ОписаниеОграничения = Контекст.ДополнительныйКонтекст.ОписанияОграничений.Получить(ПолноеИмя);
	
	Если ОписаниеОграничения = Неопределено Тогда
		СтруктураОграничения = Неопределено;
	Иначе
		СтруктураОграничения = РассчитаннаяСтруктураОграничения(ПолноеИмя,
			ОписаниеОграничения.Текст, ОписаниеОграничения.ВМодулеМенеджера, Контекст.ДляВнешнихПользователей);
	КонецЕсли;
	
	Возврат ПараметрыОграниченияПоСтруктуреОграничения(ПолноеИмя,
		СтруктураОграничения,
		Контекст.ДляВнешнихПользователей,
		Контекст.ОбщийКонтекст,
		Контекст.ДополнительныйКонтекст);
	
КонецФункции

// Для процедуры ДобавитьХранимыеПараметрыОграниченияДляВидаПользователей.
Процедура УстановитьУровеньЗависимыхСписков(СвойстваВедущегоСписка, СвойстваСписков, ПредыдущиеВедущие,
			МаксимальныйУровень)
	
	ПредыдущиеВедущие.Добавить(СвойстваВедущегоСписка.ПолноеИмя);
	
	Для Каждого ЗависимыйСписок Из СвойстваВедущегоСписка.Зависимые Цикл
		Если ПредыдущиеВедущие.Найти(ЗависимыйСписок) <> Неопределено Тогда
			УчастникиЦикла = "";
			Индекс = ПредыдущиеВедущие.Количество() - 1;
			Пока Истина Цикл
				УчастникиЦикла = "- " + ПредыдущиеВедущие[Индекс] + Символы.ПС + УчастникиЦикла;
				Если ПредыдущиеВедущие[Индекс] = ЗависимыйСписок Тогда
					Прервать;
				КонецЕсли;
				Индекс = Индекс - 1;
			КонецЦикла;
			УчастникиЦикла = УчастникиЦикла + "(!) " + ЗависимыйСписок;
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ограничения доступа, заполненные в процедурах %1
				           |модулей менеджеров или общем модуле %2,
				           |содержат недопустимую циклическую зависимость при использовании функции
				           |%3 или %4 в одном или нескольких
				           |списках-участниках цикла:
				           |%5'"),
				"ПриЗаполненииОграниченияДоступа",
				"УправлениеДоступомПереопределяемый",
				"ЧтениеОбъектаРазрешено",
				"ИзменениеОбъектаРазрешено",
				УчастникиЦикла);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		СвойстваЗависимогоСписка = СвойстваСписков.Получить(ЗависимыйСписок);
		Если СвойстваЗависимогоСписка.Уровень < СвойстваВедущегоСписка.Уровень + 1 Тогда
			СвойстваЗависимогоСписка.Уровень = СвойстваВедущегоСписка.Уровень + 1;
			Если МаксимальныйУровень < СвойстваЗависимогоСписка.Уровень Тогда
				МаксимальныйУровень = СвойстваЗависимогоСписка.Уровень;
			КонецЕсли;
		КонецЕсли;
		Если Не СвойстваЗависимогоСписка.Ведущий Тогда
			СвойстваЗависимогоСписка.Обработан = Истина;
			Продолжить;
		КонецЕсли;
		УстановитьУровеньЗависимыхСписков(СвойстваЗависимогоСписка, СвойстваСписков, ПредыдущиеВедущие, МаксимальныйУровень);
	КонецЦикла;
	
	ПредыдущиеВедущие.Удалить(ПредыдущиеВедущие.Количество() - 1);
	СвойстваВедущегоСписка.Обработан = Истина;
	
КонецПроцедуры

// Для процедуры ДобавитьХранимыеПараметрыОграниченияДляВидаПользователей.
Процедура НастроитьОптимизациюПоПолюВладельцу(СвойстваЗависимогоСписка, СвойстваСписков, Контекст)
	
	СвойстваЗависимогоСписка.ОграничениеПоВладельцуВозможно =
		СвойстваЗависимогоСписка.Параметры.ПолеВладельца <> Неопределено;
	
	СвойстваЗависимогоСписка.ОграничениеПоВладельцуВключено =
		     СвойстваЗависимогоСписка.ОграничениеПоВладельцуВозможно
		И Не СвойстваЗависимогоСписка.ОграничениеПоВладельцуОтключено;
	
	ЗависимыйСписокСОптимизацией = СвойстваЗависимогоСписка.ОграничениеПоВладельцуВключено;
	
	Для Каждого ВедущийСписок Из СвойстваЗависимогоСписка.Ведущие Цикл
		СвойстваВедущегоСписка = СвойстваСписков.Получить(ВедущийСписок); // См. СвойстваСписковДляРасчетаХранимыхПараметров
		Если СвойстваВедущегоСписка.Параметры.ДоступЗапрещен Тогда
			Продолжить;
		КонецЕсли;
		Если Не СвойстваВедущегоСписка.Зависимый Тогда
			Если ЗависимыйСписокСОптимизацией Тогда
				СвойстваВедущегоСписка.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа = Истина;
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		Если ЗависимыйСписокСОптимизацией Тогда
			Если СвойстваВедущегоСписка.Параметры.ПолеВладельца <> Неопределено
			   И Не СвойстваВедущегоСписка.Параметры.ПолеВладельца.Отключено Тогда
				Если СвойстваВедущегоСписка.Параметры.ТребуетсяОграничениеПоВладельцу Тогда
					ИмяПризнакаОптимизации = ?(Контекст.ДляВнешнихПользователей,
						"ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей", "ПоВладельцуБезЗаписиКлючейДоступа");
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"", но такая
						           |оптимизация невозможна, так как она требуется для зависимого списка ""%2"".'"),
						ИмяПризнакаОптимизации,
						СвойстваЗависимогоСписка.ПолноеИмя);
					КонтекстОшибки = Новый Структура("СпискиСОграничением, ОписанияОграничений");
					КонтекстОшибки.Вставить("Список",                  СвойстваВедущегоСписка.ПолноеИмя);
					КонтекстОшибки.Вставить("ОписанияОграничений",     Контекст.ДополнительныйКонтекст.ОписанияОграничений);
					КонтекстОшибки.Вставить("ДляВнешнихПользователей", Контекст.ДляВнешнихПользователей);
					ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, КонтекстОшибки);
					ВызватьИсключение ТекстОшибки;
				Иначе
					СвойстваВедущегоСписка.ОграничениеПоВладельцуОтключено = Истина;
					СвойстваВедущегоСписка.Параметры.ПолеВладельца.Отключено = Истина;
					УстановитьСвойствоОграничения(СвойстваВедущегоСписка.ПолноеИмя,
						"ПолеВладельца", СвойстваВедущегоСписка.Параметры.ПолеВладельца, Контекст);
				КонецЕсли;
			КонецЕсли;
			СвойстваВедущегоСписка.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа = Истина;
		КонецЕсли;
		НастроитьОптимизациюПоПолюВладельцу(СвойстваВедущегоСписка, СвойстваСписков, Контекст);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьХранимыеПараметрыОграниченияДляВидаПользователей.
Процедура УстановитьСвойствоОграничения(ПолноеИмя, ИмяСвойства, ЗначениеСвойства, Контекст)
	
	Свойства = СвойстваОграниченияСписка(ПолноеИмя, Контекст.ДополнительныйКонтекст, Истина);
	
	Свойства[ИмяСвойства] = ЗначениеСвойства;
	
КонецПроцедуры

// Для процедур УстановитьСвойствоОграничения, ЗаполнитьЗапросыПроверкиПравЧтениеИзменение.
Функция СвойстваОграниченияСписка(ПолноеИмя, Контекст, ДобавлятьВКоллекцию = Ложь)
	
	Свойства = Контекст.СвойстваОграниченияСписков.Получить(ПолноеИмя);
	
	Если Свойства = Неопределено Тогда
		Свойства = НовыеСвойстваОграниченияСписка();
		Если ДобавлятьВКоллекцию Тогда
			Контекст.СвойстваОграниченияСписков.Вставить(ПолноеИмя, Свойства);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Свойства;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ДоступЗапрещен                  - Булево
//     * ПолеВладельца                   - см. НовоеПолеВладельца 
//     * ОпорныеПоля                     - см. НовоеОписаниеОпорныхПолей
//     * ИмяОтдельногоРегистраКлючей     - Строка
//     * РассчитыватьПраваПользователей  - Булево
//     * ИспользуемыеТипыЗначенийДоступа - ХранилищеЗначения - содержит тип Массив из Тип
//     * ОграничениеВШаблонахЧерезКлючиДоступаПользователей              - Булево
//     * ОграничениеВШаблонахЧерезКлючиДоступаПользователейИГруппДоступа - Булево
//
Функция НовыеСвойстваОграниченияСписка()
	
	Свойства = Новый Структура;
	Свойства.Вставить("ДоступЗапрещен", Ложь);
	Свойства.Вставить("ПолеВладельца",  Неопределено);
	Свойства.Вставить("ОпорныеПоля",    Неопределено);
	Свойства.Вставить("ИмяОтдельногоРегистраКлючей", "");
	Свойства.Вставить("РассчитыватьПраваПользователей", Ложь);
	Свойства.Вставить("ОграничениеВШаблонахЧерезКлючиДоступаПользователей", Ложь);
	Свойства.Вставить("ОграничениеВШаблонахЧерезКлючиДоступаПользователейИГруппДоступа", Ложь);
	Свойства.Вставить("ИспользуемыеТипыЗначенийДоступа", Неопределено);
	
	Возврат Свойства;
	
КонецФункции

// Для функции ХранимыеПараметрыОграниченияДоступа.
Процедура ДобавитьВедущиеРоли(ВедущиеРоли, ИмяСвойстваВидаПользователей, КонтекстВидаПользователей)
	
	Для Каждого ОписаниеВедущейРоли Из КонтекстВидаПользователей.ВедущиеРоли Цикл
		ИмяРоли = ОписаниеВедущейРоли.Ключ;
		ЗависимыеСписки = ВедущиеРоли.Получить(ИмяРоли);
		Если ЗависимыеСписки = Неопределено Тогда
			ЗависимыеСписки = Новый Структура("ДляПользователей, ДляВнешнихПользователей");
			ВедущиеРоли.Вставить(ИмяРоли, ЗависимыеСписки);
		КонецЕсли;
		ЗависимыеСписки[ИмяСвойстваВидаПользователей] =
			Новый ФиксированноеСоответствие(ОписаниеВедущейРоли.Значение);
	КонецЦикла;
	
КонецПроцедуры

// Для функции ХранимыеПараметрыОграниченияДоступа.
Процедура ДобавитьВедущиеСписки(ВедущиеСписки, ИмяСвойстваВидаПользователей, ЗависимыйСписок,
			ВедущиеСпискиЗависимогоСписка)
	
	Для Каждого ОписаниеВедущегоСписка Из ВедущиеСпискиЗависимогоСписка.ПоЗначениямПолей Цикл
		ВедущийСписок = ТекущиеСвойстваВедущегоСписка(ВедущиеСписки, ОписаниеВедущегоСписка.Ключ);
		ВедущийСписок.ЗависимыеСписки.Вставить(ЗависимыйСписок, Истина);
		ДобавляемыеПоля = ОписаниеВедущегоСписка.Значение;
		
		Если ВедущийСписок.ПоЗначениямПолей = Неопределено Тогда
			ПоЗначениямПолей = Новый Структура;
			ПоЗначениямПолей.Вставить("ЭтоСсылочныйТип", ДобавляемыеПоля.ЭтоСсылочныйТип);
			ПоЗначениямПолей.Вставить("ПоляШапки", ОписаниеПолейВедущегоСписка());
			Если ПоЗначениямПолей.ЭтоСсылочныйТип Тогда
				ПоЗначениямПолей.Вставить("ТабличныеЧасти", Новый Соответствие);
			КонецЕсли;
			ВедущийСписок.ПоЗначениямПолей = ПоЗначениямПолей;
		Иначе
			ПоЗначениямПолей = ВедущийСписок.ПоЗначениямПолей;
		КонецЕсли;
		
		ДобавитьПоляВедущегоСписка(ПоЗначениямПолей.ПоляШапки, ДобавляемыеПоля.ДляОтслеживания.ПоляШапки,
			ДобавляемыеПоля.ДляОтбора.ПоляШапки, ЗависимыйСписок, ИмяСвойстваВидаПользователей);
		
		Для Каждого ТабличнаяЧасть Из ДобавляемыеПоля.ДляОтслеживания.ТабличныеЧасти Цикл
			ОписаниеТабличнойЧасти = ПоЗначениямПолей.ТабличныеЧасти.Получить(ТабличнаяЧасть.Ключ);
			Если ОписаниеТабличнойЧасти = Неопределено Тогда
				ОписаниеТабличнойЧасти = ОписаниеПолейВедущегоСписка();
				ОписаниеТабличнойЧасти.Вставить("Имя", ТабличнаяЧасть.Ключ);
				ПоЗначениямПолей.ТабличныеЧасти.Вставить(ТабличнаяЧасть.Ключ, ОписаниеТабличнойЧасти);
			КонецЕсли;
			ТабличнаяЧастьПоляОтбора = ДобавляемыеПоля.ДляОтбора.ТабличныеЧасти.Получить(ТабличнаяЧасть.Ключ);
			ДобавитьПоляВедущегоСписка(ОписаниеТабличнойЧасти, ТабличнаяЧасть.Значение,
				ТабличнаяЧастьПоляОтбора, ЗависимыйСписок, ИмяСвойстваВидаПользователей);
		КонецЦикла;
	КонецЦикла;
	
	ДобавитьВедущиеСпискиПоВидуЗависимости(ВедущиеСписки,
		ИмяСвойстваВидаПользователей, ЗависимыйСписок, ВедущиеСпискиЗависимогоСписка, "ПоКлючамДоступа");
	
	ДобавитьВедущиеСпискиПоВидуЗависимости(ВедущиеСписки,
		ИмяСвойстваВидаПользователей, ЗависимыйСписок, ВедущиеСпискиЗависимогоСписка, "ПоЗначениямСГруппами");
	
КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//     * ЭтоСсылочныйТип - Булево
//     * ПоляШапки       - см. ОписаниеПолейВедущегоСписка
//     * ТабличныеЧасти  - Массив из см. ОписаниеПолейВедущегоСписка
//
Функция ВедущийСписокПоЗначениямПолей()
	
	Возврат Новый Структура;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ДляПользователей        - Массив из Строка - полные имена списков
//     * ДляВнешнихПользователей - Массив из Строка - полные имена списков
//
Функция ВедущийСписокПоКлючамДоступаИлиЗначениямСГруппами()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедуры ДобавитьВедущиеСписки.
//
// Возвращаемое значение:
//   Структура:
//     * ВсеПоля       - Массив
//     * ТипыВсехПолей - Соответствие из КлючИЗначение:
//        ** Ключ - Строка - имя поля.
//        ** Значение - ХранилищеЗначения - содержит тип ОписаниеТипов - тип поля.
//     * НаборыПолей   - Структура:
//         ** ДляПользователей - Булево
//         ** ДляВнешнихПользователей - Булево
//     * Имя - Строка - имя табличной части (есть только у полей табличной части)
//
Функция ОписаниеПолейВедущегоСписка()
	
	Описание = Новый Структура;
	Описание.Вставить("ВсеПоля",       Новый Массив);
	Описание.Вставить("ТипыВсехПолей", Новый Соответствие);
	Описание.Вставить("НаборыПолей",   Новый Структура("ДляПользователей, ДляВнешнихПользователей"));
	
	Возврат Описание;
	
КонецФункции

// Для процедуры ДобавитьВедущиеСписки.
Процедура ДобавитьВедущиеСпискиПоВидуЗависимости(ВедущиеСписки, ИмяСвойстваВидаПользователей,
			ЗависимыйСписок, ВедущиеСпискиЗависимогоСписка, ВидЗависимости)
	
	Для Каждого ОписаниеВедущегоСписка Из ВедущиеСпискиЗависимогоСписка[ВидЗависимости] Цикл
		ВедущийСписок = ТекущиеСвойстваВедущегоСписка(ВедущиеСписки, ОписаниеВедущегоСписка.Ключ);
		ВедущийСписок.ЗависимыеСписки.Вставить(ЗависимыйСписок, Истина);
		
		Если ВедущийСписок[ВидЗависимости] = Неопределено Тогда
			Свойства = ВедущийСписокПоКлючамДоступаИлиЗначениямСГруппами();
			Свойства.Вставить("ДляПользователей",        Неопределено);
			Свойства.Вставить("ДляВнешнихПользователей", Неопределено);
			ВедущийСписок[ВидЗависимости] = Свойства;
		Иначе
			Свойства = ВедущийСписок[ВидЗависимости];
		КонецЕсли;
		
		Если Свойства[ИмяСвойстваВидаПользователей] = Неопределено Тогда
			Свойства[ИмяСвойстваВидаПользователей] = Новый Массив;
		КонецЕсли;
		ЗависимыеСписки = Свойства[ИмяСвойстваВидаПользователей]; // Массив 
		ЗависимыеСписки.Добавить(ЗависимыйСписок);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьВедущиеСписки.
//
// Возвращаемое значение:
//   Структура:
//     * ЗависимыеСписки      - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * ПоЗначениямПолей     - Структура - похожая на см. ВедущийСписокПоПоЗначениямПолей
//     * ПоКлючамДоступа      - см. ВедущийСписокПоКлючамДоступаИлиЗначениямСГруппами
//     * ПоЗначениямСГруппами - см. ВедущийСписокПоКлючамДоступаИлиЗначениямСГруппами
//
Функция ТекущиеСвойстваВедущегоСписка(ВедущиеСписки, ПолноеИмя)
	
	ВедущийСписок = ВедущиеСписки.Получить(ПолноеИмя);
	Если ВедущийСписок = Неопределено Тогда
		ВедущийСписок = Новый Структура;
		ВедущийСписок.Вставить("ЗависимыеСписки",      Новый Соответствие);
		ВедущийСписок.Вставить("ПоЗначениямПолей",     Неопределено);
		ВедущийСписок.Вставить("ПоКлючамДоступа",      Неопределено);
		ВедущийСписок.Вставить("ПоЗначениямСГруппами", Неопределено);
		ВедущиеСписки.Вставить(ПолноеИмя, ВедущийСписок)
	КонецЕсли;
	
	Возврат ВедущийСписок;
	
КонецФункции

// Для процедуры ДобавитьВедущиеСписки.
Процедура ДобавитьПоляВедущегоСписка(ТекущиеПоля, ОписаниеПолей, ОписаниеПолейОтбора, ЗависимыйСписок,
			ИмяСвойстваВидаПользователей)
	
	СписокПолей = Новый СписокЗначений;
	Для Каждого ОписаниеПоля Из ОписаниеПолей Цикл
		Если ТекущиеПоля.ВсеПоля.Найти(ОписаниеПоля.Ключ) = Неопределено Тогда
			ТекущиеПоля.ВсеПоля.Добавить(ОписаниеПоля.Ключ);
			ТекущиеПоля.ТипыВсехПолей.Вставить(ОписаниеПоля.Ключ, ОписаниеПоля.Значение);
		КонецЕсли;
		СписокПолей.Добавить(ОписаниеПоля.Ключ);
	КонецЦикла;
	СписокПолей.СортироватьПоЗначению();
	НаборПолей = СтрСоединить(СписокПолей.ВыгрузитьЗначения(), ", ");
	
	НаборыПолей = ТекущиеПоля.НаборыПолей[ИмяСвойстваВидаПользователей];
	Если НаборыПолей = Неопределено Тогда
		НаборыПолей = Новый Соответствие;
		ТекущиеПоля.НаборыПолей[ИмяСвойстваВидаПользователей] = НаборыПолей;
	КонецЕсли;
	ЗависимыеСпискиПоПолямОтбора = НаборыПолей.Получить(НаборПолей);
	Если ЗависимыеСпискиПоПолямОтбора = Неопределено Тогда
		ЗависимыеСпискиПоПолямОтбора = Новый Соответствие;
		НаборыПолей.Вставить(НаборПолей, ЗависимыеСпискиПоПолямОтбора);
	КонецЕсли;
	
	СписокПолей = Новый СписокЗначений;
	Для Каждого ОписаниеПоля Из ОписаниеПолейОтбора Цикл
		СписокПолей.Добавить(ОписаниеПоля.Ключ);
	КонецЦикла;
	СписокПолей.СортироватьПоЗначению();
	НаборПолейОтбора = СтрСоединить(СписокПолей.ВыгрузитьЗначения(), ", ");
	
	ЗависимыеСписки = ЗависимыеСпискиПоПолямОтбора.Получить(НаборПолейОтбора);
	Если ЗависимыеСписки = Неопределено Тогда
		ЗависимыеСписки = Новый Массив;
		ЗависимыеСпискиПоПолямОтбора.Вставить(НаборПолейОтбора, ЗависимыеСписки);
	КонецЕсли;
	ЗависимыеСписки.Добавить(ЗависимыйСписок);
	
КонецПроцедуры

// Для процедуры ДобавитьХранимыеПараметрыОграниченияДляВидаПользователей.
Процедура НастроитьПараметрыШаблонов(СвойстваСписка, Контекст)
	
	Параметры = СвойстваСписка.Параметры;
	ПараметрыШаблонов = Контекст.ПараметрыШаблонов;
	
	Если Параметры.ДоступЗапрещен Тогда
		Уточнение = ?(Контекст.ДляВнешнихПользователей
			И Не Контекст.ОбщийКонтекст.ВнешниеПользователиВключены, "1", "0");
		ПараметрыШаблонов.СпискиСОграничениемЧерезКлючиДоступаГруппДоступа =
			ПараметрыШаблонов.СпискиСОграничениемЧерезКлючиДоступаГруппДоступа
				+ СвойстваСписка.ПолноеИмя + ";" + Уточнение + Символы.ПС;
		Возврат;
	КонецЕсли;
	
	Если Не Параметры.ИспользуетсяОграничениеПоВладельцу
	   И Не Параметры.ЭтоСсылочныйТип
	   И Не Параметры.ОграничениеОтключено Тогда
		
		ОпорныеПоля = Параметры.ОпорныеПоля;
		Версия = НоваяВерсияПараметровШаблонов();
		Версия.ДатаСоздания   = Контекст.ДатаСоздания;
		Версия.Список         = СвойстваСписка.ПолноеИмя;
		Версия.ПоляСоединения = СтрСоединить(ОпорныеПоля.Используемые, ",");
		Версия.ПоляШаблона    = СтрСоединить(ОпорныеПоля.Все, ",");
		Версия.ВариантДоступа = ОпорныеПоля.Используемые.Количество() * 2
			+ ?(Контекст.ДляВнешнихПользователей, 1, 0);
		Версии = Новый Массив;
		Версии.Добавить(Версия);
		КлючТаблицы = КлючТаблицы(СвойстваСписка.ПолноеИмя, Параметры.Контекст.ИмяКоллекцииТипа);
		Контекст.ВерсииПараметровШаблонов.Вставить(КлючТаблицы, Версии);
	КонецЕсли;
	
	Если Параметры.ОграничениеЧтенияОтключено Тогда
		ПараметрыШаблонов.СпискиСОтключеннымОграничениемЧтения =
			ПараметрыШаблонов.СпискиСОтключеннымОграничениемЧтения
				+ СвойстваСписка.ПолноеИмя + ";" + Символы.ПС;
		Возврат;
	КонецЕсли;
	
	ПолеВладельцаОтключено = "";
	
	Если Параметры.ИспользуетсяОграничениеПоВладельцу
	 Или Параметры.ЭтоСсылочныйТип Тогда
		
		ПолеВладельца = Параметры.ПолеВладельца; // См. НовоеПолеВладельца
		Если ПолеВладельца <> Неопределено Тогда
			Поля = ":" + ПолеВладельца.Имя;
			Если Не Параметры.ИспользуетсяОграничениеПоВладельцу Тогда
				ПолеВладельцаОтключено = ";-";
			КонецЕсли;
		Иначе
			Поля = ":";
		КонецЕсли;
	Иначе
		ОпорныеПоля = Параметры.ОпорныеПоля;
		Если ЗначениеЗаполнено(Параметры.ИмяОтдельногоРегистраКлючей) Тогда
			Поля = "КлючиДоступаКРегистру" + СтрРазделить(СвойстваСписка.ПолноеИмя, ".")[1];
		Иначе
			Поля = УправлениеДоступомСлужебныйПовтИсп.ОписаниеПредопределенногоИдентификатораОбъектаМетаданных(
				СвойстваСписка.ПолноеИмя);
		КонецЕсли;
		Поля = ":[" + Поля + "]";
		Номер = 1;
		Для Каждого ИмяПоля Из ОпорныеПоля.Все Цикл
			Поля = Поля + ":" + ИмяПоля;
			Номер = Номер + 1;
		КонецЦикла;
		НомерПустогоПоля = ОпорныеПоля.Все.Количество() + 1;
		Для Номер = НомерПустогоПоля По ОпорныеПоля.МаксимальноДопустимоеКоличество Цикл
			Поля = Поля + ":";
		КонецЦикла;
	КонецЕсли;
	
	ПараметрыШаблонов.СпискиСОграничениемЧерезКлючиДоступаГруппДоступа =
		ПараметрыШаблонов.СпискиСОграничениемЧерезКлючиДоступаГруппДоступа
			+ СвойстваСписка.ПолноеИмя + Поля + ";*" + ПолеВладельцаОтключено + Символы.ПС;
	
	Если СвойстваСписка.КлючиДоступаПользователейИГруппДоступа
	 Или СвойстваСписка.КлючиДоступаПользователей Тогда
		
		ПараметрыШаблонов.СпискиСОграничениемЧерезКлючиДоступаПользователей =
			ПараметрыШаблонов.СпискиСОграничениемЧерезКлючиДоступаПользователей
				+ СвойстваСписка.ПолноеИмя + Поля + ";+" + ПолеВладельцаОтключено + Символы.ПС;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры НастроитьПараметрыШаблонов.
//
// Параметры:
//  ПолноеИмя - Строка
//  ИмяКоллекцииТипа - Строка
//  ТипыТаблицПоИменам - Соответствие
//
// Возвращаемое значение:
//  Строка
//
Функция КлючТаблицы(ПолноеИмя, ИмяКоллекцииТипа = Неопределено, ТипыТаблицПоИменам = Неопределено)
	
	Если ИмяКоллекцииТипа = Неопределено Тогда
		СвойстваТипа = ТипыТаблицПоИменам.Получить(ВРег(СтрРазделить(ПолноеИмя, ".", Ложь)[0]));
		ИмяКоллекцииТипа = СвойстваТипа.ИмяКоллекции;
	КонецЕсли;
	
	Если ИмяКоллекцииТипа = "РегистрыСведений"
	 Или ИмяКоллекцииТипа = "РегистрыНакопления"
	 Или ИмяКоллекцииТипа = "РегистрыБухгалтерии"
	 Или ИмяКоллекцииТипа = "РегистрыРасчета" Тогда
		
		ТипКлючаТаблицы = ТипКлючаЗаписиПоПолномуИмениМетаданных(ПолноеИмя);
		
	ИначеЕсли ИмяКоллекцииТипа = "Последовательности" Тогда
		ТипКлючаТаблицы = ТипНабораЗаписейПоПолномуИмениМетаданных(ПолноеИмя);
		
	ИначеЕсли ИмяКоллекцииТипа = "ЖурналыДокументов" Тогда
		ТипКлючаТаблицы = ТипМенеджераОбъектаПоПолномуИмениМетаданных(ПолноеИмя);
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Неизвестный нессылочный объект метаданных
			           |""%1""
			           |с поддержкой ограничений на уровне записей.'"), ПолноеИмя);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.SHA256);
	СтрокаДляХеширования = СтрокаДанныхДляХеширования(ТипКлючаТаблицы);
	ХешированиеДанных.Добавить(СтрокаДляХеширования);
	
	Возврат Base64Строка(ХешированиеДанных.ХешСумма);
	
КонецФункции

// Для процедуры ОписаниеНовойВерсииПараметровОграниченияДоступа.
Процедура ЗаполнитьПараметрыДляШаблонов(Запись, ПараметрыЗаписи, ОписаниеВерсии)
	
	Если ТипЗнч(ОписаниеВерсии.ДатаСоздания) = Тип("Дата") Тогда
		СтараяДатаСоздания = ОписаниеВерсии.ДатаСоздания;
	Иначе
		СтараяДатаСоздания = '00010101';
	КонецЕсли;
	
	ХранимыеТекущиеВерсии = НоваяСтруктураХранимыхВерсийПараметровШаблонов();
	ТекущиеСвойстваОграниченияСписковДляПользователей        = Новый Соответствие;
	ТекущиеСвойстваОграниченияСписковДляВнешнихПользователей = Новый Соответствие;
	ВерсииПараметровШаблоновПолучены = Ложь;
	
	Если ТипЗнч(ОписаниеВерсии.ВерсииПараметровШаблонов) = Тип("ХранилищеЗначения") Тогда
		СодержимоеХранилища = ЗначениеИзХранилища(ОписаниеВерсии.ВерсииПараметровШаблонов);
		Если ЗначениеЗаполнено(СодержимоеХранилища) Тогда
			ХранимыеТекущиеВерсии = СтруктураХранимыхВерсийПараметровШаблонов(СодержимоеХранилища);
			Если ХранимыеТекущиеВерсии.ВерсияСтруктурыВерсий = ВерсияСтруктурыВерсийПараметровШаблонов() Тогда
				ВерсииПараметровШаблоновПолучены = Истина;
				ТекущиеСвойстваОграниченияСписковДляПользователей        = Неопределено;
				ТекущиеСвойстваОграниченияСписковДляВнешнихПользователей = Неопределено;
			Иначе
				ХранимыеТекущиеВерсии = НоваяСтруктураХранимыхВерсийПараметровШаблонов();
			КонецЕсли;
		ИначеЕсли ТипЗнч(ОписаниеВерсии.Версия) = Тип("Число") Тогда
			ТекущиеХранимыеПараметры = ВерсияПараметров(ОписаниеВерсии.Версия, Ложь, Ложь);
			Если ТипЗнч(ТекущиеХранимыеПараметры.ДляЗаписиОбъектовИПроверкиПрав) = Тип("ХранилищеЗначения") Тогда
				ТекущиеПараметрыЗаписи = СтруктураХранимыхПараметровЗаписи(
					ЗначениеИзХранилища(ТекущиеХранимыеПараметры.ДляЗаписиОбъектовИПроверкиПрав));
				Если ТекущиеПараметрыЗаписи.ВерсияСтруктурыКэша = ВерсияСтруктурыКэша()
				 Или ТекущиеПараметрыЗаписи.ВерсияСтруктурыКэша = "20" Тогда
					ТекущиеСвойстваОграниченияСписковДляПользователей =
						ТекущиеПараметрыЗаписи.ДополнительныйКонтекст.ДляПользователей.СвойстваОграниченияСписков;
					ТекущиеСвойстваОграниченияСписковДляВнешнихПользователей =
						ТекущиеПараметрыЗаписи.ДополнительныйКонтекст.ДляВнешнихПользователей.СвойстваОграниченияСписков;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	Если Не ВерсииПараметровШаблоновПолучены Тогда
		ОписаниеВерсии.ХешСумма = "#" + ОписаниеВерсии.ХешСумма;
	КонецЕсли;
	
	Если ПараметрыЗаписи.Свойство("СтрокиВерсии") Тогда
		СтрокиВерсии = ПараметрыЗаписи.СтрокиВерсии;
	Иначе
		СтрокиВерсии = Новый Структура;
	КонецЕсли;
	СтрокиВерсии.Вставить("СпискиСОграничениемПоПолямВСеансахПользователей");
	СтрокиВерсии.Вставить("СпискиСОграничениемПоПолямВСеансахВнешнихПользователей");
	
	Если ТекущиеСвойстваОграниченияСписковДляПользователей <> Неопределено Тогда
		НовыеПараметрыЗаписи = ПараметрыЗаписи.ХранимыеПараметры.ДляЗаписиОбъектовИПроверкиПрав.Получить();
		НовыеСвойстваОграниченияСписковДляПользователей =
			НовыеПараметрыЗаписи.ДополнительныйКонтекст.ДляПользователей.СвойстваОграниченияСписков;
		НовыеСвойстваОграниченияСписковДляВнешнихПользователей =
			НовыеПараметрыЗаписи.ДополнительныйКонтекст.ДляВнешнихПользователей.СвойстваОграниченияСписков;
	КонецЕсли;
	
	ХранимыеНовыеВерсии = ПараметрыЗаписи.ХранимыеПараметры.ВерсииПараметровШаблонов.Получить();
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.SHA256);
	
	Параметры = НоваяСтруктураДляЗаполненияПараметровШаблонов();
	Если ТипЗнч(ПараметрыЗаписи.СпискиСУстаревшимиВариантамиДоступа) = Тип("Массив") Тогда
		Параметры.Вставить("ОтключитьДополнительныеВариантыДоступа");
	Иначе
		ПараметрыЗаписи.СпискиСУстаревшимиВариантамиДоступа = Новый Массив;
	КонецЕсли;
	Параметры.Вставить("СтараяДатаСоздания", СтараяДатаСоздания);
	Параметры.Вставить("ХешированиеДанных",  ХешированиеДанных);
	Параметры.Вставить("СпискиСНовымОсновнымВариантомДоступа", ПараметрыЗаписи.СпискиСНовымОсновнымВариантомДоступа);
	Параметры.Вставить("СпискиСУстаревшимиВариантамиДоступа",  ПараметрыЗаписи.СпискиСУстаревшимиВариантамиДоступа);
	
	Параметры.Вставить("ХранимыеТекущиеВерсии",   ХранимыеТекущиеВерсии.ДляПользователей);
	Параметры.Вставить("НовыеВерсииПолейСписков", ХранимыеНовыеВерсии.ДляПользователей.ВерсииПараметровШаблонов);
	Параметры.Вставить("ТекущиеСвойстваОграниченияСписков", ТекущиеСвойстваОграниченияСписковДляПользователей);
	Параметры.Вставить("НовыеСвойстваОграниченияСписков",   НовыеСвойстваОграниченияСписковДляПользователей);
	
	ЗаполнитьПараметрыДляШаблоновДляВидаПользователей(Параметры,
		Запись.ДляШаблоновВСеансахПользователей,
		СтрокиВерсии.СпискиСОграничениемПоПолямВСеансахПользователей);
	
	Параметры.ХранимыеТекущиеВерсии   = ХранимыеТекущиеВерсии.ДляВнешнихПользователей;
	Параметры.НовыеВерсииПолейСписков = ХранимыеНовыеВерсии.ДляВнешнихПользователей.ВерсииПараметровШаблонов;
	Параметры.ТекущиеСвойстваОграниченияСписков = ТекущиеСвойстваОграниченияСписковДляВнешнихПользователей;
	Параметры.НовыеСвойстваОграниченияСписков   = НовыеСвойстваОграниченияСписковДляВнешнихПользователей;
	
	ЗаполнитьПараметрыДляШаблоновДляВидаПользователей(Параметры,
		Запись.ДляШаблоновВСеансахВнешнихПользователей,
		СтрокиВерсии.СпискиСОграничениемПоПолямВСеансахВнешнихПользователей);
	
	Запись.ХешСуммаПараметровШаблонов = Base64Строка(ХешированиеДанных.ХешСумма);
	Запись.ВерсииПараметровШаблонов = Новый ХранилищеЗначения(ХранимыеТекущиеВерсии);
	
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.SHA256);
	ХешированиеДанных.Добавить(Запись.ХешСуммаПостоянныхПараметров);
	ХешированиеДанных.Добавить(Запись.ХешСуммаПараметровШаблонов);
	Запись.ХешСумма = Base64Строка(ХешированиеДанных.ХешСумма);
	
КонецПроцедуры

// Для процедуры ЗаполнитьПараметрыДляШаблонов.
//
// Возвращаемое значение:
//  Структура:
//   * СтараяДатаСоздания - Дата
//   * ХешированиеДанных  - ХешированиеДанных
//   * СпискиСНовымОсновнымВариантомДоступа - Массив из Строка
//   * СпискиСУстаревшимиВариантамиДоступа  - Массив из Строка
//   * ХранимыеТекущиеВерсии - Структура:
//      ** ВерсииПараметровШаблонов - см. НовыеВерсииПараметровШаблонов
//      ** ОсновныеВариантыДоступа  - см. НовыеОсновныеВариантыДоступа
//   * НовыеВерсииПолейСписков - см. НовыеВерсииПараметровШаблонов
//   * ТекущиеСвойстваОграниченияСписков - Соответствие из КлючИЗначение:
//      ** Ключ     - Строка - полное имя списка
//      ** Значение - см. НовыеСвойстваОграниченияСписка
//   * НовыеСвойстваОграниченияСписков - Соответствие из КлючИЗначение:
//      ** Ключ     - Строка - полное имя списка
//      ** Значение - см. НовыеСвойстваОграниченияСписка
//
Функция НоваяСтруктураДляЗаполненияПараметровШаблонов()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедуры ЗаполнитьПараметрыДляШаблонов.
// 
// Параметры:
//  Параметры - см. НоваяСтруктураДляЗаполненияПараметровШаблонов
//  ХранилищеДляШаблоновВСеансах - ХранилищеЗначения - обновляемое значение
//  СпискиСОграничениемПоПолям - Строка - возвращаемое значение
//
Процедура ЗаполнитьПараметрыДляШаблоновДляВидаПользователей(Параметры,
			ХранилищеДляШаблоновВСеансах, СпискиСОграничениемПоПолям)
	
	МаксимальноеКоличествоВерсий = МаксимальноеКоличествоВерсийВВариантеДоступа();
	НастройкиОграниченийСписков = Новый СписокЗначений;
	ТекущиеВерсииПолейСписков = Параметры.ХранимыеТекущиеВерсии.ВерсииПараметровШаблонов;
	
	Если Параметры.ТекущиеСвойстваОграниченияСписков <> Неопределено Тогда
		ВариантыДоступаВБазеДанных = ВариантыДоступаВБазеДанных(Параметры);
		Для Каждого КлючИЗначение Из Параметры.НовыеВерсииПолейСписков Цикл
			ТекущиеВерсииПолейСписка = Новый Массив;
			ДобавитьСуществующиеВерсииПолейСписка(ТекущиеВерсииПолейСписка,
				КлючИЗначение.Значение[0], ВариантыДоступаВБазеДанных, Параметры);
			Если ЗначениеЗаполнено(ТекущиеВерсииПолейСписка) Тогда
				ТекущиеВерсииПолейСписков.Вставить(КлючИЗначение.Ключ, ТекущиеВерсииПолейСписка);
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ВозможноНеиспользуемыеВерсииПолейСписков = Новый Массив;
	
	Для Каждого КлючИЗначение Из ТекущиеВерсииПолейСписков Цикл
		ТекущиеВерсииПолейСписка = КлючИЗначение.Значение;
		НовыеВерсииПолейСписка = Параметры.НовыеВерсииПолейСписков.Получить(КлючИЗначение.Ключ);
		
		Если НовыеВерсииПолейСписка = Неопределено Тогда
			ИспользованиеОтключалось = Ложь;
			Для Каждого Версия Из ТекущиеВерсииПолейСписка Цикл
				Если Версия.Используется Тогда
					Версия.Используется = Ложь;
					ИспользованиеОтключалось = Истина;
				КонецЕсли;
			КонецЦикла;
			Если ИспользованиеОтключалось И ТекущиеВерсииПолейСписка.Количество() > 0 Тогда
				ПолноеИмя = ТекущиеВерсииПолейСписка[0].Список;
				Если ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя) <> Неопределено Тогда
					Параметры.СпискиСУстаревшимиВариантамиДоступа.Добавить(
						ТекущиеВерсииПолейСписка[0].Список);
				КонецЕсли;
			КонецЕсли;
			Продолжить;
		КонецЕсли;
		
		НоваяВерсия = НовыеВерсииПолейСписка[0];
		НоваяВерсияСуществует = Ложь;
		Для Каждого Версия Из ТекущиеВерсииПолейСписка Цикл
			Если ВРег(Версия.ПоляСоединения) = ВРег(НоваяВерсия.ПоляСоединения)
			   И Версия.ВариантДоступа > 1 Тогда
				НоваяВерсияСуществует = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		
		ОсновнаяВерсияИзменена = Истина;
		Если НоваяВерсияСуществует Тогда
			Индекс = ТекущиеВерсииПолейСписка.Найти(Версия);
			Если Индекс > 0 Тогда
				ТекущиеВерсииПолейСписка.Удалить(Индекс);
				ТекущиеВерсииПолейСписка.Вставить(0, Версия);
			Иначе
				ОсновнаяВерсияИзменена = Ложь;
			КонецЕсли;
			ЗаполнитьЗначенияСвойств(Версия, НоваяВерсия,, "ВариантДоступа");
		Иначе
			ИспользуемыеНомераВерсий = Новый Массив(МаксимальноеКоличествоВерсий);
			СамаяСтараяВерсия = Неопределено;
			УдаляемыеВерсии = Новый Массив;
			Для Каждого Версия Из ТекущиеВерсииПолейСписка Цикл
				НомерВерсии = Цел(Версия.ВариантДоступа / 64);
				Если НоваяВерсия.ВариантДоступа <> (Версия.ВариантДоступа - НомерВерсии * 64) Тогда
					Продолжить;
				КонецЕсли;
				Если НомерВерсии < МаксимальноеКоличествоВерсий Тогда
					ИспользуемыеНомераВерсий[НомерВерсии] = Версия;
					СамаяСтараяВерсия = Версия;
				Иначе
					УдаляемыеВерсии.Добавить(Версия);
				КонецЕсли;
			КонецЦикла;
			Для Индекс = 0 По МаксимальноеКоличествоВерсий - 1 Цикл
				Если ИспользуемыеНомераВерсий[Индекс] = Неопределено Тогда
					Прервать;
				КонецЕсли;
			КонецЦикла;
			Если Индекс >= МаксимальноеКоличествоВерсий Тогда
				УдаляемыеВерсии.Добавить(СамаяСтараяВерсия);
				НоваяВерсия.ВариантДоступа = НоваяВерсия.ВариантДоступа
					+ ИспользуемыеНомераВерсий.Найти(СамаяСтараяВерсия) * 64;
			Иначе
				НоваяВерсия.ВариантДоступа = НоваяВерсия.ВариантДоступа + Индекс * 64;
			КонецЕсли;
			Для Каждого УдаляемаяВерсия Из УдаляемыеВерсии Цикл
				ТекущиеВерсииПолейСписка.Удалить(ТекущиеВерсииПолейСписка.Найти(УдаляемаяВерсия));
			КонецЦикла;
			ТекущиеВерсииПолейСписка.Вставить(0, НоваяВерсия);
		КонецЕсли;
		
		Если ОсновнаяВерсияИзменена Тогда
			Параметры.СпискиСНовымОсновнымВариантомДоступа.Добавить(
				ТекущиеВерсииПолейСписка[0].Список);
		КонецЕсли;
		
		ОбновитьИспользованиеВерсийПолейСписка(ТекущиеВерсииПолейСписка, ОсновнаяВерсияИзменена,
			ВозможноНеиспользуемыеВерсииПолейСписков, Параметры);
	КонецЦикла;
	
	ОтключитьИспользованиеВерсийКромеОсновнойДляОбновленныхСписков(
		ВозможноНеиспользуемыеВерсииПолейСписков, Параметры);
	
	ОсновныеВариантыДоступа = Параметры.ХранимыеТекущиеВерсии.ОсновныеВариантыДоступа;
	
	Для Каждого КлючИЗначение Из Параметры.НовыеВерсииПолейСписков Цикл
		ТекущиеВерсииПолейСписка = ТекущиеВерсииПолейСписков.Получить(КлючИЗначение.Ключ);
		Если ТекущиеВерсииПолейСписка = Неопределено Тогда
			ТекущиеВерсииПолейСписка = КлючИЗначение.Значение;
			ТекущиеВерсииПолейСписков.Вставить(КлючИЗначение.Ключ, ТекущиеВерсииПолейСписка);
			Параметры.СпискиСНовымОсновнымВариантомДоступа.Добавить(
				ТекущиеВерсииПолейСписка[0].Список);
		КонецЕсли;
		ИспользуемыеВариантыДоступа = Новый Массив;
		Для Каждого ВерсияПолейСписка Из ТекущиеВерсииПолейСписка Цикл
			Если ВерсияПолейСписка.Используется Тогда
				ИспользуемыйВариантДоступа = НовыйИспользуемыйВариантДоступа();
				ИспользуемыйВариантДоступа.ВариантДоступа = ВерсияПолейСписка.ВариантДоступа;
				ИспользуемыйВариантДоступа.ПоляСоединения = ВерсияПолейСписка.ПоляСоединения;
				ИспользуемыеВариантыДоступа.Добавить(Новый ФиксированнаяСтруктура(ИспользуемыйВариантДоступа));
			КонецЕсли;
		КонецЦикла;
		ОсновныеВариантыДоступа.Вставить(ТекущиеВерсииПолейСписка[0].Список,
			Новый ФиксированныйМассив(ИспользуемыеВариантыДоступа));
	КонецЦикла;
	
	Для Каждого КлючИЗначение Из ТекущиеВерсииПолейСписков Цикл
		ДобавитьПоляОграниченияСписка(НастройкиОграниченийСписков, КлючИЗначение.Значение);
	КонецЦикла;
	
	НастройкиОграниченийСписков.СортироватьПоПредставлению();
	СпискиСОграничениемПоПолям = СтрСоединить(НастройкиОграниченийСписков.ВыгрузитьЗначения(), Символы.ПС);
	Параметры.ХешированиеДанных.Добавить(СпискиСОграничениемПоПолям);
	
	ХранимыеПараметрыШаблонов = Новый Структура(ХранилищеДляШаблоновВСеансах.Получить());
	ПараметрыШаблонов = Новый Структура(ХранимыеПараметрыШаблонов.ПараметрыШаблонов);
	ПараметрыШаблонов.СпискиСОграничениемПоПолям = СпискиСОграничениемПоПолям;
	ХранимыеПараметрыШаблонов.ПараметрыШаблонов = Новый ФиксированнаяСтруктура(ПараметрыШаблонов);
	ХранилищеДляШаблоновВСеансах = Новый ХранилищеЗначения(
		Новый ФиксированнаяСтруктура(ХранимыеПараметрыШаблонов));
	
КонецПроцедуры

// Для процедуры ЗаполнитьПараметрыДляШаблоновДляВидаПользователей.
Процедура ОбновитьИспользованиеВерсийПолейСписка(ТекущиеВерсииПолейСписка, ОсновнаяВерсияИзменена,
			ВозможноНеиспользуемыеВерсииПолейСписков, Параметры)
	
	МаксимальноеКоличествоВариантовСоединений = МаксимальноеКоличествоВариантовСоединений();
	НоваяВерсия = ТекущиеВерсииПолейСписка[0];
	ПоляШаблона = СтрРазделить(ВРег(НоваяВерсия.ПоляШаблона), ",", Ложь);
	
	КоличествоИспользуемыхВерсий = 0;
	ИспользованиеОтключалось = Ложь;
	
	Для Каждого ТекущаяВерсия Из ТекущиеВерсииПолейСписка Цикл
		Если Не ТекущаяВерсия.Используется Тогда
			Продолжить;
		КонецЕсли;
		Если ВРег(ТекущаяВерсия.ПоляШаблона) <> ВРег(НоваяВерсия.ПоляШаблона) Тогда
			ИменаПолей = СтрРазделить(ТекущаяВерсия.ПоляСоединения, ",", Ложь);
			Для Каждого ИмяПоля Из ИменаПолей Цикл
				Если ПоляШаблона.Найти(ВРег(ИмяПоля)) = Неопределено Тогда
					ТекущаяВерсия.Используется = Ложь;
					ИспользованиеОтключалось = Истина;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Если Не ТекущаяВерсия.Используется Тогда
			Продолжить;
		КонецЕсли;
		Если КоличествоИспользуемыхВерсий >= МаксимальноеКоличествоВариантовСоединений Тогда
			ТекущаяВерсия.Используется = Ложь;
			ИспользованиеОтключалось = Истина;
		Иначе
			КоличествоИспользуемыхВерсий = КоличествоИспользуемыхВерсий + 1;
		КонецЕсли;
	КонецЦикла;
	
	Если ИспользованиеОтключалось Тогда
		Параметры.СпискиСУстаревшимиВариантамиДоступа.Добавить(
			ТекущиеВерсииПолейСписка[0].Список);
	КонецЕсли;
	
	Если Не ОсновнаяВерсияИзменена И КоличествоИспользуемыхВерсий > 1 Тогда
		ВозможноНеиспользуемыеВерсииПолейСписков.Добавить(ТекущиеВерсииПолейСписка);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьПараметрыДляШаблоновДляВидаПользователей.
Процедура ДобавитьПоляОграниченияСписка(СпискиСОграничениемПоПолям, ТекущиеВерсииПолейСписка)
	
	НоваяВерсия = ТекущиеВерсииПолейСписка[0];
	ПолноеИмя = НоваяВерсия.Список;
	ПоляШаблона = СтрРазделить(ВРег(НоваяВерсия.ПоляШаблона), ",", Ложь);
	НастройкиОграниченийСписка = Новый Массив;
	МаксимальноеКоличествоВариантов = МаксимальноеКоличествоВариантовСоединений();
	
	НомерВарианта = 1;
	Для Каждого Версия Из ТекущиеВерсииПолейСписка Цикл
		Если Не Версия.Используется Тогда
			Прервать;
		КонецЕсли;
		
		НомерПоляСоединения = 1;
		ИменаПолей = СтрРазделить(Версия.ПоляСоединения, ",", Ложь);
		Для Каждого ИмяПоля Из ИменаПолей Цикл
			НомерПоляШаблона = ПоляШаблона.Найти(ВРег(ИмяПоля)) + 1;
			НастройкиОграниченийСписка.Добавить(СтрШаблон("%1:%2%3=%4;",
				ПолноеИмя,
				"Вариант" + XMLСтрока(НомерВарианта),
				"Поле" + XMLСтрока(НомерПоляСоединения),
				"Поле" + XMLСтрока(НомерПоляШаблона)));
			НомерПоляСоединения = НомерПоляСоединения + 1;
		КонецЦикла;
		
		Если Цел(Версия.ВариантДоступа / 2) = 0 Тогда
			НастройкиОграниченийСписка.Добавить(СтрШаблон("%1:%2%3=0;",
				ПолноеИмя,
				"Вариант" + XMLСтрока(НомерВарианта),
				"Версия" + XMLСтрока(НомерВарианта)));
		Иначе
			Остаток = Цел(Версия.ВариантДоступа / 64);
			НомерБита = 0;
			Пока Остаток > 0 Цикл
				НовыйОстаток = Цел(Остаток / 2);
				Если Остаток - НовыйОстаток * 2 > 0 Тогда
					НастройкиОграниченийСписка.Добавить(СтрШаблон("%1:%2%3=1;",
						ПолноеИмя,
						"Версия" + XMLСтрока(НомерВарианта),
						"Бит" + XMLСтрока(НомерБита)));
				КонецЕсли;
				Остаток = НовыйОстаток;
				НомерБита = НомерБита + 1;
			КонецЦикла;
		КонецЕсли;
		
		НомерВарианта = НомерВарианта + 1;
		Если НомерВарианта > МаксимальноеКоличествоВариантов Тогда
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	СпискиСОграничениемПоПолям.Добавить(
		СтрСоединить(НастройкиОграниченийСписка, Символы.ПС), ПолноеИмя);
	
КонецПроцедуры

// Для процедуры ЗаполнитьПараметрыДляШаблоновДляВидаПользователей.
Функция ВариантыДоступаВБазеДанных(Параметры)
	
	Если Не ЗначениеЗаполнено(Параметры.НовыеВерсииПолейСписков) Тогда
		Возврат Новый Соответствие;
	КонецЕсли;
	
	ТекстОбщегоЗапроса =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ТекущаяТаблица.Регистр КАК Регистр,
	|	ТекущаяТаблица.ВариантДоступа КАК ВариантДоступа
	|ИЗ
	|	РегистрСведений.КлючиДоступаКРегистрам КАК ТекущаяТаблица
	|ГДЕ
	|	ВЫБОР
	|			КОГДА ТекущаяТаблица.ВариантДоступа = 0
	|				ТОГДА ЛОЖЬ
	|			ИНАЧЕ ТекущаяТаблица.ВариантДоступа - (ВЫРАЗИТЬ(ТекущаяТаблица.ВариантДоступа / 2 - 0.5 КАК ЧИСЛО(15, 0))) * 2 = 1
	|		КОНЕЦ = &ДляВнешнихПользователей
	|
	|УПОРЯДОЧИТЬ ПО
	|	Регистр,
	|	ВариантДоступа";
	
	ТекстыЗапросов = Новый Массив;
	ТекстыЗапросов.Добавить(ТекстОбщегоЗапроса);
	
	ПолныеИмена = Новый Массив;
	ИндексыРезультатовЗапроса = Новый Соответствие;
	
	Для Каждого КлючИЗначение Из Параметры.НовыеВерсииПолейСписков Цикл
		Список = КлючИЗначение.Значение[0].Список;
		СвойстваСписка = Параметры.НовыеСвойстваОграниченияСписков.Получить(Список);
		Если СвойстваСписка = Неопределено
		 Или Не ЗначениеЗаполнено(СвойстваСписка.ОпорныеПоля) Тогда
			Продолжить;
		КонецЕсли;
		ПолныеИмена.Добавить(Список);
		Если ЗначениеЗаполнено(СвойстваСписка.ИмяОтдельногоРегистраКлючей) Тогда
			ИндексыРезультатовЗапроса.Вставить(Список, ТекстыЗапросов.Количество());
			ТекстЗапроса =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ТекущаяТаблица.ВариантДоступа КАК ВариантДоступа
			|ИЗ
			|	&ТекущаяТаблица КАК ТекущаяТаблица
			|ГДЕ
			|	ВЫБОР
			|			КОГДА ТекущаяТаблица.ВариантДоступа = 0
			|				ТОГДА ЛОЖЬ
			|			ИНАЧЕ ТекущаяТаблица.ВариантДоступа - (ВЫРАЗИТЬ(ТекущаяТаблица.ВариантДоступа / 2 - 0.5 КАК ЧИСЛО(15, 0))) * 2 = 1
			|		КОНЕЦ = &ДляВнешнихПользователей
			|
			|УПОРЯДОЧИТЬ ПО
			|	ВариантДоступа";
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущаяТаблица",
				"РегистрСведений." + СвойстваСписка.ИмяОтдельногоРегистраКлючей);
			ТекстыЗапросов.Добавить(ТекстЗапроса);
		Иначе
			ИндексыРезультатовЗапроса.Вставить(Список, 0);
		КонецЕсли;
	КонецЦикла;
	
	ИдентификаторыСписков = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(ПолныеИмена, Ложь);
	
	Запрос = Новый Запрос;
	ВариантДоступа = КлючИЗначение.Значение[0].ВариантДоступа;
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ВариантДоступа - Цел(ВариантДоступа / 2) * 2 > 0);
	Запрос.Текст = СтрСоединить(ТекстыЗапросов, ОбщегоНазначения.РазделительПакетаЗапросов());
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	Выгрузка = РезультатыЗапроса[0].Выгрузить();
	Выгрузка.Индексы.Добавить("Регистр");
	
	Результат = Новый Соответствие;
	
	Для Каждого КлючИЗначение Из ИндексыРезультатовЗапроса Цикл
		Если КлючИЗначение.Значение = 0 Тогда
			ИдентификаторСписка = ИдентификаторыСписков.Получить(КлючИЗначение.Ключ);
			НайденныеСтроки = Выгрузка.НайтиСтроки(Новый Структура("Регистр", ИдентификаторСписка));
			ВариантыДоступа = Новый Массив;
			Для Каждого НайденнаяСтрока Из НайденныеСтроки Цикл
				ВариантыДоступа.Добавить(НайденнаяСтрока.ВариантДоступа);
			КонецЦикла;
			Результат.Вставить(КлючИЗначение.Ключ, ВариантыДоступа);
		Иначе
			Результат.Вставить(КлючИЗначение.Ключ,
				РезультатыЗапроса[КлючИЗначение.Значение].Выгрузить().ВыгрузитьКолонку("ВариантДоступа"));
		КонецЕсли;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Для процедуры ЗаполнитьПараметрыДляШаблоновДляВидаПользователей.
Процедура ОтключитьИспользованиеВерсийКромеОсновнойДляОбновленныхСписков(
			ВозможноНеиспользуемыеВерсииПолейСписков, Параметры)
	
	Если Не ЗначениеЗаполнено(ВозможноНеиспользуемыеВерсииПолейСписков)
	 Или Не Параметры.Свойство("ОтключитьДополнительныеВариантыДоступа") Тогда
		Возврат;
	КонецЕсли;
	
	Списки = Новый Массив;
	ВерсииПолейСписков = Новый Соответствие;
	Для Каждого ВерсииПолейСписка Из ВозможноНеиспользуемыеВерсииПолейСписков Цикл
		Список = ВерсииПолейСписка[0].Список;
		Списки.Добавить(Список);
		ВерсииПолейСписков.Вставить(Список, ВерсииПолейСписка);
	КонецЦикла;
	
	ДляВнешнихПользователей = ВерсииПолейСписка[0].ВариантДоступа
		- Цел(ВерсииПолейСписка[0].ВариантДоступа / 2) * 2 = 1;
	
	ТипыИдентификаторов = Новый Массив;
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных"));
	ТипыИдентификаторов.Добавить(Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений"));
	
	ИдентификаторыСписков = Новый ТаблицаЗначений;
	ИдентификаторыСписков.Колонки.Добавить("ИдентификаторСписка", Новый ОписаниеТипов(ТипыИдентификаторов));
	
	СпискиПоИдентификаторам = Новый Соответствие;
	ИдентификаторыОбъектов = ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(Списки, Ложь);
	
	Для Каждого КлючИЗначение Из ИдентификаторыОбъектов Цикл
		Если ЗначениеЗаполнено(КлючИЗначение.Значение) Тогда
			ИдентификаторыСписков.Добавить().ИдентификаторСписка = КлючИЗначение.Значение;
			СпискиПоИдентификаторам.Вставить(КлючИЗначение.Значение, КлючИЗначение.Ключ);
		КонецЕсли;
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("ДляВнешнихПользователей", ДляВнешнихПользователей);
	Запрос.УстановитьПараметр("ИдентификаторыСписков",   ИдентификаторыСписков);
	Запрос.Текст =
	"ВЫБРАТЬ
	|	ИдентификаторыСписков.ИдентификаторСписка КАК ИдентификаторСписка
	|ПОМЕСТИТЬ ИдентификаторыСписков
	|ИЗ
	|	&ИдентификаторыСписков КАК ИдентификаторыСписков
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	ИдентификаторыСписков.ИдентификаторСписка КАК ИдентификаторСписка
	|ИЗ
	|	ИдентификаторыСписков КАК ИдентификаторыСписков
	|ГДЕ
	|	НЕ ИСТИНА В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ИСТИНА
	|				ИЗ
	|					РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК ОбновлениеКлючейДоступаКДанным
	|				ГДЕ
	|					ОбновлениеКлючейДоступаКДанным.Список = ИдентификаторыСписков.ИдентификаторСписка
	|					И ОбновлениеКлючейДоступаКДанным.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|					И ОбновлениеКлючейДоступаКДанным.РазмерЗадания = 3)";
	
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		Список = СпискиПоИдентификаторам.Получить(Выборка.ИдентификаторСписка);
		ВерсииПолейСписка = ВерсииПолейСписков.Получить(Список);
		ОсновнаяВерсия = ВерсииПолейСписка[0];
		Для Каждого ВерсияПолейСписка Из ВерсииПолейСписка Цикл
			Если ВерсияПолейСписка = ОсновнаяВерсия Тогда
				Продолжить;
			КонецЕсли;
			Если ВерсияПолейСписка.Используется Тогда
				ВерсияПолейСписка.Используется = Ложь;
				Параметры.СпискиСУстаревшимиВариантамиДоступа.Добавить(Список);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ЗаполнитьПараметрыДляШаблоновДляВидаПользователей.
Процедура ДобавитьСуществующиеВерсииПолейСписка(ТекущиеВерсииПолейСписка, НоваяВерсия,
			ВариантыДоступаВБазеДанных, Параметры)
	
	ВариантыДоступа = ВариантыДоступаВБазеДанных.Получить(НоваяВерсия.Список);
	Если Не ЗначениеЗаполнено(ВариантыДоступа) Тогда
		Возврат;
	КонецЕсли;
	
	ТекущиеСвойстваСписка = Параметры.ТекущиеСвойстваОграниченияСписков.Получить(НоваяВерсия.Список);
	ОпорныеПоля = ?(ТекущиеСвойстваСписка = Неопределено, Неопределено, ТекущиеСвойстваСписка.ОпорныеПоля);
	Если ЗначениеЗаполнено(Параметры.СтараяДатаСоздания) И ЗначениеЗаполнено(ОпорныеПоля) Тогда
		ТребуемыйВариантДоступа = ОпорныеПоля.Используемые.Количество() * 2
			+ НоваяВерсия.ВариантДоступа - Цел(НоваяВерсия.ВариантДоступа / 2) * 2;
	Иначе
		ТребуемыйВариантДоступа = НоваяВерсия.ВариантДоступа;
	КонецЕсли;
	
	Если ВариантыДоступа.Количество() = 1
	   И (Не ЗначениеЗаполнено(Параметры.СтараяДатаСоздания)
	      Или ЗначениеЗаполнено(ОпорныеПоля))
	   И (    ВариантыДоступа[0] < 2
	      Или ВариантыДоступа[0] - Цел(ВариантыДоступа[0] / 64) * 64 = ТребуемыйВариантДоступа) Тогда
		
		// Переход с версий конфигурации без поля ВариантДоступа или восстановление
		// либо после очистки регистра ПараметрыОграниченияДоступа,
		// либо после изменения номера в функции ВерсияСтруктурыВерсийПараметровШаблонов.
		Если Не ЗначениеЗаполнено(Параметры.СтараяДатаСоздания) Тогда
			ПоляСоединения = НоваяВерсия.ПоляСоединения;
			ПоляШаблона    = НоваяВерсия.ПоляСоединения;
		Иначе
			ПоляСоединения = СтрСоединить(ОпорныеПоля.Используемые, ",");
			ПоляШаблона    = СтрСоединить(ОпорныеПоля.Все, ",");
		КонецЕсли;
		
		ПредыдущаяВерсия = НоваяВерсияПараметровШаблонов();
		ПредыдущаяВерсия.ДатаСоздания   = Параметры.СтараяДатаСоздания;
		ПредыдущаяВерсия.Список         = НоваяВерсия.Список;
		ПредыдущаяВерсия.ПоляСоединения = ПоляСоединения;
		ПредыдущаяВерсия.ПоляШаблона    = ПоляШаблона;
		ПредыдущаяВерсия.ВариантДоступа = ВариантыДоступа[0];
		
		ТекущиеВерсииПолейСписка.Добавить(ПредыдущаяВерсия);
	Иначе
		// Несколько версий существующих одновременно не могут быть сопоставлены
		// одному набору полей и не могут использоваться для новых наборов полей.
		Для Каждого ВариантДоступа Из ВариантыДоступа Цикл
			НеизвестнаяВерсия = НоваяВерсияПараметровШаблонов();
			НеизвестнаяВерсия.ДатаСоздания   = Параметры.СтараяДатаСоздания;
			НеизвестнаяВерсия.Список         = НоваяВерсия.Список;
			НеизвестнаяВерсия.ВариантДоступа = ВариантДоступа;
			НеизвестнаяВерсия.Используется   = Ложь;
			
			ТекущиеВерсииПолейСписка.Добавить(НеизвестнаяВерсия);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ОбновитьИспользованиеВерсийПолейСписка и ДобавитьПоляОграниченияСписка.
Функция МаксимальноеКоличествоВариантовСоединений()
	
	// При изменении нужно синхронно изменить шаблон ограничения доступа ДляРегистра.
	Возврат 3;
	
КонецФункции

// Для процедур ЗаполнитьПараметрыДляШаблоновДляВидаПользователей.
Функция МаксимальноеКоличествоВерсийВВариантеДоступа()
	
	Возврат 16;
	
КонецФункции

// Для процедур УстановитьВерсиюПараметров, ЗаполнитьПараметрыДляШаблонов,
// ОбновитьТаблицыГруппДоступаДляПодключенныхРасширений и
// функции СпискиСИзменениемВерсий.
//
Функция ЗначениеИзХранилища(ХранилищеЗначения)
	
	Попытка
		Значение = ХранилищеЗначения.Получить();
	Исключение
		Значение = Неопределено;
	КонецПопытки;
	
	Возврат Значение;
	
КонецФункции

// Для процедуры УстановитьВерсиюПараметров, и для функции ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//    * ВерсияСтруктурыКэша         - см. ВерсияСтруктурыКэша
//    * ВерсииОграниченийСписков    - ФиксированноеСоответствие
//    * ВедущиеСписки               - ФиксированноеСоответствие
//    * ДополнительныйКонтекст      - ФиксированнаяСтруктура
//    * СпискиСДатой                - ФиксированноеСоответствие
//    * ВнешниеПользователиВключены - Булево
//    * ОграничениеДоступаВключено  - Булево
//    * ИспользуемыеТипыЗначений    - ХранилищеЗначения
//
Функция СтруктураХранимыхПараметровЗаписи(Значения)
	
	ХранимыеПараметрыЗаписи = НоваяСтруктураХранимыхПараметровЗаписи();
	ХранимыеПараметрыЗаписи.ВерсияСтруктурыКэша = "";
	
	Если ТипЗнч(Значения)                             = Тип("ФиксированнаяСтруктура")
	   И Значения.Свойство("ВерсияСтруктурыКэша")
	   И ТипЗнч(Значения.ВерсияСтруктурыКэша)         = Тип("Строка")
	   И Значения.Свойство("ВерсииОграниченийСписков")
	   И ТипЗнч(Значения.ВерсииОграниченийСписков)    = Тип("ФиксированноеСоответствие")
	   И Значения.Свойство("ВедущиеСписки")
	   И ТипЗнч(Значения.ВедущиеСписки)               = Тип("ФиксированноеСоответствие")
	   И Значения.Свойство("ДополнительныйКонтекст")
	   И ТипЗнч(Значения.ДополнительныйКонтекст)      = Тип("ФиксированнаяСтруктура")
	   И Значения.Свойство("СпискиСДатой")
	   И ТипЗнч(Значения.СпискиСДатой)                = Тип("ФиксированноеСоответствие")
	   И Значения.Свойство("ВнешниеПользователиВключены")
	   И ТипЗнч(Значения.ВнешниеПользователиВключены) = Тип("Булево")
	   И Значения.Свойство("ОграничениеДоступаВключено")
	   И ТипЗнч(Значения.ОграничениеДоступаВключено)  = Тип("Булево")
	   И Значения.Свойство("ИспользуемыеТипыЗначений")
	   И ТипЗнч(Значения.ИспользуемыеТипыЗначений)    = Тип("ХранилищеЗначения") Тогда
		
		ЗаполнитьЗначенияСвойств(ХранимыеПараметрыЗаписи, Значения);
	КонецЕсли;
	
	Возврат Новый ФиксированнаяСтруктура(ХранимыеПараметрыЗаписи);
	
КонецФункции

// Для функций СтруктураХранимыхПараметровЗаписи и ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//  Структура:
//    * ВерсияСтруктурыКэша         - см. ВерсияСтруктурыКэша
//    * ВерсииОграниченийСписков    - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - полное имя списка
//        ** Значение - Строка - общая версия ограничения списка 
//                               первая строка - хеш-сумма свойств версии для пользователей и через Символы.ПС
//                               вторая строка - хеш-сумма свойств версии для внешних пользователей.
//    
//    * ВедущиеСписки - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - полное имя списка
//        ** Значение - см. СвойстваСпискаКакВедущего
//    
//    * ДополнительныйКонтекст - Структура:
//        ** ДляПользователей        - см. НовыйХранимыйДополнительныйКонтекст
//        ** ДляВнешнихПользователей - см. НовыйХранимыйДополнительныйКонтекст
//    
//    * СпискиСДатой - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - полное имя списка
//        ** Значение - Булево - Истина
//    * ВнешниеПользователиВключены - Булево
//    * ОграничениеДоступаВключено  - Булево
//    * ИспользуемыеТипыЗначений    - ХранилищеЗначения - смотри функцию ИспользуемыеТипыЗначений.
//
Функция НоваяСтруктураХранимыхПараметровЗаписи()
	
	ХранимыеПараметрыЗаписи = Новый Структура;
	ХранимыеПараметрыЗаписи.Вставить("ВерсияСтруктурыКэша",         ВерсияСтруктурыКэша());
	ХранимыеПараметрыЗаписи.Вставить("ВерсииОграниченийСписков",    Новый Соответствие);
	ХранимыеПараметрыЗаписи.Вставить("ВедущиеСписки",               Новый Соответствие);
	ХранимыеПараметрыЗаписи.Вставить("ДополнительныйКонтекст",      Новый Структура);
	ХранимыеПараметрыЗаписи.Вставить("СпискиСДатой",                Новый Соответствие);
	ХранимыеПараметрыЗаписи.Вставить("ВедущиеРоли",                 Новый Соответствие);
	ХранимыеПараметрыЗаписи.Вставить("ВнешниеПользователиВключены", Ложь);
	ХранимыеПараметрыЗаписи.Вставить("ОграничениеДоступаВключено",  Ложь);
	ХранимыеПараметрыЗаписи.Вставить("ИспользуемыеТипыЗначений",    Новый ХранилищеЗначения(Новый Соответствие));
	
	Возврат ХранимыеПараметрыЗаписи;
	
КонецФункции

// Для процедуры УстановитьВерсиюПараметров.
//
// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//    * ВерсияСтруктурыКэша - см. ВерсияСтруктурыКэша
//    * ВерсииШаблонов      - см. ВерсииШаблоновОграниченияДоступа
//    * ПараметрыШаблонов   - см. СтруктураПараметровШаблонов
//
Функция СтруктураХранимыхПараметровШаблонов(Значения)
	
	ХранимыеПараметрыШаблонов = НоваяСтруктураХранимыхПараметровШаблонов();
	ХранимыеПараметрыШаблонов.ВерсияСтруктурыКэша = "";
	
	Если ТипЗнч(Значения)                     = Тип("ФиксированнаяСтруктура")
	   И Значения.Свойство("ВерсияСтруктурыКэша")
	   И ТипЗнч(Значения.ВерсияСтруктурыКэша) = Тип("Строка")
	   И Значения.Свойство("ВерсииШаблонов")
	   И ТипЗнч(Значения.ВерсииШаблонов)      = Тип("Строка")
	   И Значения.Свойство("ПараметрыШаблонов")
	   И ТипЗнч(Значения.ПараметрыШаблонов)   = Тип("ФиксированнаяСтруктура") Тогда
		
		ЗаполнитьЗначенияСвойств(ХранимыеПараметрыШаблонов, Значения);
	КонецЕсли;
	
	Возврат Новый ФиксированнаяСтруктура(ХранимыеПараметрыШаблонов);
	
КонецФункции

// Для функций СтруктураХранимыхПараметровШаблонов, СтруктураХранимыхПараметровЗаписи и
// ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//  Структура:
//    * ВерсияСтруктурыКэша - см. ВерсияСтруктурыКэша
//    * ВерсииШаблонов      - см. ВерсииШаблоновОграниченияДоступа
//    * ПараметрыШаблонов   - см. СтруктураПараметровШаблонов
//
Функция НоваяСтруктураХранимыхПараметровШаблонов()
	
	ХранимыеПараметрыШаблонов = Новый Структура;
	ХранимыеПараметрыШаблонов.Вставить("ВерсияСтруктурыКэша", ВерсияСтруктурыКэша());
	ХранимыеПараметрыШаблонов.Вставить("ВерсииШаблонов",      ВерсииШаблоновОграниченияДоступа());
	ХранимыеПараметрыШаблонов.Вставить("ПараметрыШаблонов",   НоваяСтруктураПараметровШаблонов());
	
	Возврат ХранимыеПараметрыШаблонов;
	
КонецФункции

// Для процедуры УстановитьВерсиюПараметров.
//
// Возвращаемое значение:
//   ФиксированнаяСтруктура:
//     * СпискиСОграничениемЧерезКлючиДоступаГруппДоступа  - Строка
//     * СпискиСОграничениемЧерезКлючиДоступаПользователей - Строка
//     * СпискиСОграничениемПоПолям                        - Строка
//     * СпискиСОтключеннымОграничениемЧтения              - Строка
//
Функция СтруктураПараметровШаблонов(Значения)
	
	ПараметрыШаблонов = НоваяСтруктураПараметровШаблонов();
	
	Если ТипЗнч(Значения) = Тип("ФиксированнаяСтруктура")
	   И Значения.Количество() = ПараметрыШаблонов.Количество() Тогда
		
		Совпадает = Истина;
		Для Каждого КлючИЗначение Из ПараметрыШаблонов Цикл
			Если Не Значения.Свойство(КлючИЗначение.Ключ)
			 Или Не ТипЗнч(Значения[КлючИЗначение.Ключ]) = ТипЗнч(КлючИЗначение.Значение) Тогда
				Совпадает = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Совпадает Тогда
			ЗаполнитьЗначенияСвойств(ПараметрыШаблонов, Значения);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Новый ФиксированнаяСтруктура(ПараметрыШаблонов);
	
КонецФункции

// Для функций СтруктураПараметровШаблонов, ХранимыеПараметрыОграниченияДоступа и
// НоваяСтруктураХранимыхПараметровШаблонов.
//
// Возвращаемое значение:
//   Структура:
//     * СпискиСОграничениемЧерезКлючиДоступаГруппДоступа  - Строка
//     * СпискиСОграничениемЧерезКлючиДоступаПользователей - Строка
//     * СпискиСОграничениемПоПолям                        - Строка
//     * СпискиСОтключеннымОграничениемЧтения              - Строка
//
Функция НоваяСтруктураПараметровШаблонов()
	
	ПараметрыШаблонов = Новый Структура;
	ПараметрыШаблонов.Вставить("СпискиСОграничениемЧерезКлючиДоступаГруппДоступа", "");
	ПараметрыШаблонов.Вставить("СпискиСОграничениемЧерезКлючиДоступаПользователей", "");
	ПараметрыШаблонов.Вставить("СпискиСОграничениемПоПолям", "");
	ПараметрыШаблонов.Вставить("СпискиСОтключеннымОграничениемЧтения", "");
	
	Возврат ПараметрыШаблонов;
	
КонецФункции

// Для процедур УстановитьВерсиюПараметров и ЗаполнитьПараметрыДляШаблонов.
//
// Возвращаемое значение:
//   см. НоваяСтруктураХранимыхВерсийПараметровШаблонов
//
Функция СтруктураХранимыхВерсийПараметровШаблонов(Значения)
	
	ХранимыеВерсийПараметровШаблонов = НоваяСтруктураХранимыхВерсийПараметровШаблонов();
	ХранимыеВерсийПараметровШаблонов.ВерсияСтруктурыВерсий = "";
	
	Если ТипЗнч(Значения) = Тип("Структура")
	   И Значения.Свойство("ВерсияСтруктурыВерсий")
	   И ТипЗнч(Значения.ВерсияСтруктурыВерсий) = Тип("Строка")
	   И Значения.Свойство("ДляПользователей")
	   
	   И ТипЗнч(Значения.ДляПользователей) = Тип("Структура")
	   И Значения.ДляПользователей.Свойство("ВерсииПараметровШаблонов")
	   И ТипЗнч(Значения.ДляПользователей.ВерсииПараметровШаблонов) = Тип("Соответствие")
	   И Значения.ДляПользователей.Свойство("ОсновныеВариантыДоступа")
	   И ТипЗнч(Значения.ДляПользователей.ОсновныеВариантыДоступа)  = Тип("Соответствие")
	   
	   И Значения.Свойство("ДляВнешнихПользователей")
	   И ТипЗнч(Значения.ДляВнешнихПользователей) = Тип("Структура")
	   И Значения.ДляВнешнихПользователей.Свойство("ВерсииПараметровШаблонов")
	   И ТипЗнч(Значения.ДляВнешнихПользователей.ВерсииПараметровШаблонов) = Тип("Соответствие")
	   И Значения.ДляВнешнихПользователей.Свойство("ОсновныеВариантыДоступа")
	   И ТипЗнч(Значения.ДляВнешнихПользователей.ОсновныеВариантыДоступа)  = Тип("Соответствие") Тогда
		
		ЗаполнитьЗначенияСвойств(ХранимыеВерсийПараметровШаблонов, Значения);
	КонецЕсли;
	
	Возврат ХранимыеВерсийПараметровШаблонов;
	
КонецФункции

// Для функций ХранимыеПараметрыОграниченияДоступа, СтруктураХранимыхВерсийПараметровШаблонов и
// процедуры ЗаполнитьПараметрыДляШаблонов.
//
// Возвращаемое значение:
//  Структура:
//    * ВерсияСтруктурыВерсий - см. ВерсияСтруктурыВерсийПараметровШаблонов
//    * ДляПользователей - Структура:
//       ** ВерсииПараметровШаблонов - см. НовыеВерсииПараметровШаблонов
//       ** ОсновныеВариантыДоступа  - см. НовыеОсновныеВариантыДоступа
//    * ДляВнешнихПользователей - Структура:
//       ** ВерсииПараметровШаблонов - см. НовыеВерсииПараметровШаблонов
//       ** ОсновныеВариантыДоступа  - см. НовыеОсновныеВариантыДоступа
//
Функция НоваяСтруктураХранимыхВерсийПараметровШаблонов()
	
	ДляПользователей = Новый Структура;
	ДляПользователей.Вставить("ВерсииПараметровШаблонов", НовыеВерсииПараметровШаблонов());
	ДляПользователей.Вставить("ОсновныеВариантыДоступа",  НовыеОсновныеВариантыДоступа());
	
	ДляВнешнихПользователей = Новый Структура;
	ДляВнешнихПользователей.Вставить("ВерсииПараметровШаблонов", НовыеВерсииПараметровШаблонов());
	ДляВнешнихПользователей.Вставить("ОсновныеВариантыДоступа",  НовыеОсновныеВариантыДоступа());
	
	ХранимыеПараметрыШаблонов = Новый Структура;
	ХранимыеПараметрыШаблонов.Вставить("ВерсияСтруктурыВерсий",   ВерсияСтруктурыВерсийПараметровШаблонов());
	ХранимыеПараметрыШаблонов.Вставить("ДляПользователей",        ДляПользователей);
	ХранимыеПараметрыШаблонов.Вставить("ДляВнешнихПользователей", ДляВнешнихПользователей);
	
	Возврат ХранимыеПараметрыШаблонов;
	
КонецФункции

// Для функций ХранимыеПараметрыОграниченияДоступа и
// НоваяСтруктураХранимыхВерсийПараметровШаблонов.
//
// Возвращаемое значение:
//   Соответствие из КлючИЗначение:
//     * Ключ  - Строка - полное имя регистра.
//     * Значение - Массив из см. НоваяВерсияПараметровШаблонов
//
Функция НовыеВерсииПараметровШаблонов()
	
	Возврат Новый Соответствие;
	
КонецФункции

// Для функции НовыеВерсииПараметровШаблонов и процедуры НастроитьПараметрыШаблонов.
//
// Возвращаемое значение:
//  Структура:
//   * ДатаСоздания   - Дата   - момент добавления новой версии.
//   * Список         - Строка - полное имя объекта метаданных.
//   * ПоляСоединения - Строка - список полей, используемых в соединении.
//   * ПоляШаблона    - Строка - список полей, указанных в шаблоне #ДляРегистра.
//   * Используется   - Булево - признак того, что версия используется в шаблонах.
//   * ВариантДоступа - Число  - значение поля ВариантДоступа в регистрах КлючиДоступаКРегистрам,
//                               КлючиДоступаКРегистру*.
//
Функция НоваяВерсияПараметровШаблонов()
	
	ВерсияПараметров = Новый Структура;
	ВерсияПараметров.Вставить("ДатаСоздания",   '00010101');
	ВерсияПараметров.Вставить("Список",         "");
	ВерсияПараметров.Вставить("ПоляСоединения", "");
	ВерсияПараметров.Вставить("ПоляШаблона",    "");
	ВерсияПараметров.Вставить("Используется",   Истина);
	ВерсияПараметров.Вставить("ВариантДоступа", 0);
	
	Возврат ВерсияПараметров;
	
КонецФункции

// Для функции НоваяСтруктураХранимыхВерсийПараметровШаблонов.
//
// Возвращаемое значение:
//   Соответствие из КлючИЗначение:
//     * Ключ     - Строка - полное имя регистра.
//     * Значение - Массив из см. НовыйИспользуемыйВариантДоступа
//
Функция НовыеОсновныеВариантыДоступа()
	
	Возврат Новый Соответствие;
	
КонецФункции

// Для функции ЗаполнитьПараметрыДляШаблоновДляВидаПользователей.
//
// Возвращаемое значение:
//  Структура:
//    * ВариантДоступа - Число - значение поля ВариантДоступа
//                               в регистрах КлючиДоступаКРегистрам, КлючиДоступаКРегистру*,
//                               начиная с основного варианта доступа.
//    * ПоляСоединения - Строка - имена полей соединения для варианта доступа через запятую.
//
Функция НовыйИспользуемыйВариантДоступа()
	
	Возврат Новый Структура("ВариантДоступа, ПоляСоединения");
	
КонецФункции

// Для процедуры УстановитьВерсиюПараметров.
//
// Возвращаемое значение:
//   ФиксированнаяСтруктура:
//     * ВерсияСтруктурыКэша - см. ВерсияСтруктурыКэша
//     * ВидыОграниченийПравДляПользователей        - Строка
//     * ВидыОграниченийПравДляВнешнихПользователей - Строка
//
Функция СтруктураХранимыхПараметровОтчета(Значения)
	
	ХранимыеПараметрыОтчета = НоваяСтруктураХранимыхПараметровОтчета();
	ХранимыеПараметрыОтчета.ВерсияСтруктурыКэша = "";
	
	Если ТипЗнч(Значения)                        = Тип("ФиксированнаяСтруктура")
	   И Значения.Свойство("ВерсияСтруктурыКэша")
	   И ТипЗнч(Значения.ВерсияСтруктурыКэша)    = Тип("Строка")
	   И Значения.Свойство("ВидыОграниченийПравДляПользователей")
	   И ТипЗнч(Значения.ВидыОграниченийПравДляПользователей) = Тип("Строка")
	   И Значения.Свойство("ВидыОграниченийПравДляВнешнихПользователей")
	   И ТипЗнч(Значения.ВидыОграниченийПравДляВнешнихПользователей) = Тип("Строка") Тогда
		
		ЗаполнитьЗначенияСвойств(ХранимыеПараметрыОтчета, Значения);
	КонецЕсли;
	
	Возврат Новый ФиксированнаяСтруктура(ХранимыеПараметрыОтчета);
	
КонецФункции

// Для функций СтруктураХранимыхПараметровОтчета и ХранимыеПараметрыОграниченияДоступа.
//
// Возвращаемое значение:
//   Структура:
//     * ВерсияСтруктурыКэша    - см. ВерсияСтруктурыКэша
//     * ВидыОграниченийПравДляПользователей        - Строка
//     * ВидыОграниченийПравДляВнешнихПользователей - Строка
//
Функция НоваяСтруктураХранимыхПараметровОтчета()
	
	ХранимыеПараметрыОтчета = Новый Структура;
	ХранимыеПараметрыОтчета.Вставить("ВерсияСтруктурыКэша", ВерсияСтруктурыКэша());
	ХранимыеПараметрыОтчета.Вставить("ВидыОграниченийПравДляПользователей", "");
	ХранимыеПараметрыОтчета.Вставить("ВидыОграниченийПравДляВнешнихПользователей", "");
	
	Возврат ХранимыеПараметрыОтчета;
	
КонецФункции

// Для функции ХранимыеПараметрыОграниченияДоступа.
Функция ВидыОграниченийПравСтрокой(ВидыОграниченийПрав)
	
	Список = Новый СписокЗначений;
	Для Каждого КлючИЗначение Из ВидыОграниченийПрав Цикл
		Список.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	Список.СортироватьПоЗначению();
	ВидыОграниченийСтрокой = СтрСоединить(Список.ВыгрузитьЗначения(), Символы.ПС);
	
	Возврат ВидыОграниченийСтрокой;
	
КонецФункции

#КонецОбласти

#Область ПараметрыОграниченияДоступаДляСпискаОтдельно

// Основная функция области, возвращающая параметры ограничения доступа
// для вида пользователей списка без учета зависимости от других списков,
// как по ключам доступа, так и по наличию видов доступа Пользователи и ВнешниеПользователи.
//
// Возвращаемое значение:
//   Структура:
//     * Список                  - Строка - полное имя таблицы объекта метаданных.
//     * ДляВнешнихПользователей - Булево - вид пользователей, для которых предназначены параметры.
//     * Версия                  - Строка - хеш-сумма параметров ограничения доступа для отслеживания их изменения.
//     * ВедущиеСписки           - см. НовыеВедущиеСписки
//     * ДоступЗапрещен          - Булево - Истина, если текст ограничения "ГДЕ ЛОЖЬ",
//                                          а также не указан для внешних пользователей.
//     * ОграничениеОтключено    - Булево - Истина, если текст ограничения не указан или указан,
//                                          но ограничение отключено из-за отключения использования
//                                          видов доступа, задействованных в нем.
//     * ОграничениеЧтенияОтключено - Булево - Истина, если текст ограничения чтения не указан или указан,
//                                             но ограничение отключено из-за отключения использования
//                                             видов доступа, задействованных в нем.
//                                              
//    Поле владельца, когда возможно ограничение только по объекту-владельцу.
//     * ПолеВладельца - см. НовоеПолеВладельца
//                                             
//     * ТребуетсяОграничениеПоВладельцу    - Булево - признак оптимизации, указанный разработчиком
//                                                     рядом с текстом ограничения.
//     * ИспользуетсяОграничениеПоВладельцу - Булево - признак использования оптимизации,
//                                                     вычисленный на втором проходе графа.
//     * РассчитыватьПраваПользователей     - Булево - признак расчета прав на ключи доступа для пользователей,
//                                                     а не для групп доступа, вычисленный на втором проходе графа.
//                                                     Имеет смысл только, когда признак
//                                                     ИспользуетсяОграничениеПоВладельцу = Ложь.
//     * ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа - Булево - признак того, что ведущий список должен записывать
//                                                     ключи для зависимых списков, которые не записывают
//                                                     своих ключей. Признак вычисляется на втором проходе графа.
//     * ЧтениеРазрешеноДляВсехПользователей - Булево - признак, вычисленный на втором проходе графа.
//                                                     Когда ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа = Истина,
//                                                     тогда показывает наличие права Чтение в одной из ролей
//                                                     БазовыеПрава* или БазовыеПраваВнешнихПользователей*.
//     * ИзменениеРазрешеноДляВсехПользователей - Булево - признак, вычисленный на втором проходе графа.
//                                                     Когда ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа = Истина,
//                                                     тогда показывает наличие права Изменение в одной из ролей
//                                                     БазовыеПрава* или БазовыеПраваВнешнихПользователей*.
//     * ЕстьВедущиеКлючиДоступа                - Булево - признак наличия ведущих ключей доступа в ограничении.
//     * ЕстьВедущиеСпискиПоПравам              - Булево - признак наличия ведущих списков по правам в ограничении.
//     * ЕстьФункцияПравоДоступаИлиРольДоступна - Булево - признак наличия перечисленных функций в ограничении.
//     * ТипыВладельцевНастроекПрав        - ФиксированноеСоответствие - типы владельцев настроек прав, используемые
//                                                     при расчете прав на ключи доступа (см. ПоТипамСсылок).
//     * ИдентификаторТаблицыНастроекПрав  - СправочникСсылка.ИдентификаторыОбъектовМетаданных - идентификатор
//                                             списка, если для него используются отдельные настройки прав
//                                             или пустой идентификатор.
//     * ЕстьВладельцыНастроекПрав         - Булево - признак наличия ограничения по владельцу настроек прав.
//     * ИспользуемыеТипыЗначенийДоступа   - Массив из Тип - описание типов значений доступа,
//                                                           которые используются в ограничении доступа.
//     * ВсеВидыОграниченийПрав            - Соответствие - все виды ограничений прав без учета использования.
//     * ПоляТаблицОбъекта                 - Массив из см. НовыеПоляТаблицыОбъекта
//     * ИмяОтдельногоРегистраКлючей       - Строка - для регистров.
//     * ОпорныеПоля                       - см. НовоеОписаниеОпорныхПолей
//     * ВариантДоступа                    - Число - основной вариант доступа смотри также НовыеОсновныеВариантыДоступа.
//     * СоставПолей                       - Число  - число, описывающее реквизиты, используемые в ключе.
//     * ЕстьОграничениеЧтения             - Булево - установлено, если ограничение чтения отличается от "ГДЕ ИСТИНА".
//     * ЕстьОграничениеИзменения          - Булево - установлено, если ограничение изменения отличается от "ГДЕ ИСТИНА".
//     * ЕстьОграничениеПоПользователям    - Булево - установлено, если проверяются значения Пользователь
//                                                 или ГруппаПользователей или ВнешнийПользователь
//                                                 или ГруппаВнешнихПользователей для функций
//                                                 ЗначениеРазрешено или ЭтоАвторизованныйПользователь.
//     * СтруктураРасчетаПраваЧтение       - см. СтруктураРасчетаПрава
//     * СтруктураРасчетаПраваИзменение    - см. СтруктураРасчетаПрава
//     * Контекст                          - см. КонтекстПараметровПоСтруктуреОграничения
//
Функция ПараметрыОграниченияПоСтруктуреОграничения(Список, СтруктураОграничения,
			ДляВнешнихПользователей, ОбщийКонтекст, ДополнительныйКонтекст)
	
	ВедущиеСписки = НовыеВедущиеСписки();
	
	Результат = Новый Структура;
	Результат.Вставить("Список", Список);
	Результат.Вставить("ДляВнешнихПользователей", ДляВнешнихПользователей);
	Результат.Вставить("Версия", "");
	Результат.Вставить("ВедущиеСписки", ВедущиеСписки);
	Результат.Вставить("ДоступЗапрещен", ДляВнешнихПользователей);
	Результат.Вставить("ОграничениеОтключено", Не ДляВнешнихПользователей);
	Результат.Вставить("ОграничениеЧтенияОтключено", Не ДляВнешнихПользователей);
	Результат.Вставить("ПолеВладельца");
	Результат.Вставить("ТребуетсяОграничениеПоВладельцу", Ложь);
	Результат.Вставить("ИспользуетсяОграничениеПоВладельцу", Ложь);
	Результат.Вставить("РассчитыватьПраваПользователей", Ложь);
	Результат.Вставить("ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа", Ложь);
	Результат.Вставить("ЧтениеРазрешеноДляВсехПользователей", Ложь);
	Результат.Вставить("ИзменениеРазрешеноДляВсехПользователей", Ложь);
	Результат.Вставить("ЕстьВедущиеКлючиДоступа", Ложь);
	Результат.Вставить("ЕстьВедущиеСпискиПоПравам", Ложь);
	Результат.Вставить("ЕстьФункцияПравоДоступаИлиРольДоступна", Ложь);
	Результат.Вставить("ТипыВладельцевНастроекПрав", ОбщийКонтекст.ТипыВладельцевНастроекПрав);
	Результат.Вставить("ИдентификаторТаблицыНастроекПрав", Справочники.ИдентификаторыОбъектовМетаданных.ПустаяСсылка());
	Результат.Вставить("ЕстьВладельцыНастроекПрав", Ложь);
	Результат.Вставить("ИспользуемыеТипыЗначенийДоступа", Новый Массив);
	Результат.Вставить("ВсеВидыОграниченийПрав", Новый Соответствие);
	Результат.Вставить("ПоляТаблицОбъекта", Новый Массив);
	Результат.Вставить("ИмяОтдельногоРегистраКлючей", "");
	Результат.Вставить("ОпорныеПоля");
	Результат.Вставить("ВариантДоступа");
	Результат.Вставить("СоставПолей");
	Результат.Вставить("ЕстьОграничениеЧтения", Ложь);
	Результат.Вставить("ЕстьОграничениеИзменения", Ложь);
	Результат.Вставить("ЕстьОграничениеПоПользователям", Ложь);
	Результат.Вставить("СтруктураРасчетаПраваЧтение",    СтруктураРасчетаПрава());
	Результат.Вставить("СтруктураРасчетаПраваИзменение", СтруктураРасчетаПрава());
	
	ИмяКоллекцииТипа = "";
	Результат.Вставить("ЭтоСсылочныйТип", ?(ЗначениеЗаполнено(Список),
		ЭтоСсылочныйТипТаблицы(Список, ИмяКоллекцииТипа), Ложь));
	
	Результат.Вставить("СписокСДатой",
		    ИмяКоллекцииТипа = "Документы"
		Или ИмяКоллекцииТипа = "БизнесПроцессы"
		Или ИмяКоллекцииТипа = "Задачи");
	
	Если ИмяКоллекцииТипа = "РегистрыСведений" Тогда
		МетаданныеРегистра = Метаданные.РегистрыСведений.Найти(СтрРазделить(Список, ".")[1]);
		Результат.Вставить("СписокСПериодом", ?(МетаданныеРегистра = Неопределено, Ложь,
			МетаданныеРегистра.ПериодичностьРегистраСведений
				<> Метаданные.СвойстваОбъектов.ПериодичностьРегистраСведений.Непериодический));
	Иначе
		Результат.Вставить("СписокСПериодом",
			    ИмяКоллекцииТипа = "РегистрыНакопления"
			Или ИмяКоллекцииТипа = "РегистрыБухгалтерии"
			Или ИмяКоллекцииТипа = "РегистрыРасчета");
	КонецЕсли;
	
	Контекст = КонтекстПараметровПоСтруктуреОграничения();
	Контекст.Вставить("СтрокаСвойствВерсии", "");
	Результат.Вставить("Контекст", Контекст);
	
	Для Каждого КлючИЗначение Из ОбщийКонтекст Цикл
		Контекст.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	Для Каждого КлючИЗначение Из ДополнительныйКонтекст Цикл
		Контекст.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
	КонецЦикла;
	
	Контекст.Вставить("Список",                  Список);
	Контекст.Вставить("ДляВнешнихПользователей", ДляВнешнихПользователей);
	Контекст.Вставить("ЭтоСсылочныйТип",         Результат.ЭтоСсылочныйТип);
	Контекст.Вставить("СписокСДатой",            Результат.СписокСДатой);
	Контекст.Вставить("СписокСПериодом",         Результат.СписокСПериодом);
	Контекст.Вставить("ИмяКоллекцииТипа",        ИмяКоллекцииТипа);
	Контекст.Вставить("СвойстваВерсии",          Новый Массив);
	Контекст.Вставить("ВедущиеРоли",             Новый Соответствие);
	
	ДобавитьСвойствоВерсии(Контекст, Контекст, "Список");
	ДобавитьСвойствоВерсии(Контекст, Контекст, "ДляВнешнихПользователей");
	
	ОписаниеОграничения = Контекст.ОписанияОграничений.Получить(Контекст.Список);
	Если ОписаниеОграничения = Неопределено Тогда
		ОписаниеОграничения = Новый Структура("Текст", "");
	КонецЕсли;
	ДобавитьСвойствоВерсии(Контекст, ОписаниеОграничения, "Текст");
	
	ДобавитьСвойствоВерсии(Контекст, Контекст, "ЭтоСсылочныйТип");
	ДобавитьСвойствоВерсии(Контекст, Контекст, "СписокСДатой");
	
	// Поля таблиц требуются только для ссылочных объектов.
	Контекст.Вставить("ПоляТаблицОбъекта", НовоеОписаниеПолейТаблицОбъекта(Результат));
	
	// Опорные поля не требуется для ссылочных типов данных (всегда Ссылка).
	ЗаполнитьНовоеОписаниеОпорныхПолей(Результат, Контекст);
	
	Контекст.Вставить("БезОбъектаМетаданных", Ложь);
	
	Если Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей <> Неопределено Тогда
		Свойства = СвойстваОграниченияСписка(Контекст.Список, Контекст);
		Результат.РассчитыватьПраваПользователей     = Свойства.РассчитыватьПраваПользователей;
		Результат.ИспользуетсяОграничениеПоВладельцу = Свойства.ПолеВладельца <> Неопределено
			И Не Свойства.ПолеВладельца.Отключено;
		Если Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей.Получить(Список) <> Неопределено Тогда
			Результат.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа = Истина;
			ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Список);
			Контекст.БезОбъектаМетаданных = ОбъектМетаданных = Неопределено;
			Результат.ЧтениеРазрешеноДляВсехПользователей =
				ПравоРазрешеноДляВсехПользователей("Чтение",    ОбъектМетаданных, ДляВнешнихПользователей);
			Результат.ИзменениеРазрешеноДляВсехПользователей =
				ПравоРазрешеноДляВсехПользователей("Изменение", ОбъектМетаданных, ДляВнешнихПользователей);
		КонецЕсли;
	КонецЕсли;
	Контекст.Вставить("ИспользуетсяОграничениеПоВладельцу",        Результат.ИспользуетсяОграничениеПоВладельцу);
	Контекст.Вставить("РассчитыватьПраваПользователей",            Результат.РассчитыватьПраваПользователей);
	Контекст.Вставить("ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа", Результат.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа);
	Контекст.Вставить("ЧтениеРазрешеноДляВсехПользователей",       Результат.ЧтениеРазрешеноДляВсехПользователей);
	Контекст.Вставить("ИзменениеРазрешеноДляВсехПользователей",    Результат.ИзменениеРазрешеноДляВсехПользователей);
	
	Если СтруктураОграничения = Неопределено Тогда
		Если Не Результат.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа
		 Или Результат.ДоступЗапрещен
		 Или Контекст.БезОбъектаМетаданных Тогда
			Возврат Результат;
		КонецЕсли;
		СтруктураОграничения = РассчитаннаяСтруктураОграничения(Список,
			"РазрешитьЧтениеИзменение ГДЕ ИСТИНА", Истина, ДляВнешнихПользователей);
	КонецЕсли;
	
	Контекст.Вставить("СтруктураОграничения",                    СтруктураОграничения);
	Контекст.Вставить("ВедущиеСпискиПоЗначениямПолей",           ОписаниеВедущихСписковПоЗначениямПолей());
	Контекст.Вставить("ВедущиеСпискиПоКлючамДоступа",            ОписаниеВедущихСписковПоПолюСсылка());
	Контекст.Вставить("ВедущиеСпискиПоЗначениямСГруппами",       ОписаниеВедущихСписковПоПолюСсылка());
	Контекст.Вставить("ЕстьПроверкаАвторизованногоПользователя", Ложь);
	Контекст.Вставить("НеиспользуемыеТипыЗначенийДоступа",       Новый Массив);
	Контекст.Вставить("ВсеВидыОграниченийПрав",                  Результат.ВсеВидыОграниченийПрав);
	
	Если Не Контекст.ЭтоСсылочныйТип Тогда
		Результат.ИмяОтдельногоРегистраКлючей = Контекст.ИмяОтдельногоРегистраКлючей;
	КонецЕсли;
	
	ЗаполнитьНаличиеОграниченияПоПравам(Контекст, Результат, Ложь);
	ЗаполнитьОграничениеПоОбъектуВладельцуДоУпрощения(Результат, Контекст);
	
	ЗаполнитьСвойстваПолей(Контекст);
	
	Результат.ЕстьФункцияПравоДоступаИлиРольДоступна = Контекст.ЕстьФункцияПравоДоступаИлиРольДоступна;
	
	Если ДляВнешнихПользователей И Не ОбщийКонтекст.ВнешниеПользователиВключены
	 Или ЭтоБезусловноеОграничение(Контекст, Результат) Тогда
		
		Результат.ДоступЗапрещен = Истина;
		Результат.Версия = "  ";
		Возврат Результат;
	КонецЕсли;
	
	Результат.ДоступЗапрещен = Ложь;
	Результат.ОграничениеОтключено = Истина;
	Результат.ОграничениеЧтенияОтключено = Истина;
	
	ЗаполнитьНаличиеОграниченияПоПравам(Контекст, Результат, Истина);
	ЗаполнитьОграничениеПоОбъектуВладельцуПослеУпрощения(Результат, Контекст);
	ЗаполнитьНаличиеВедущихКлючейИСписковИВладельцевНастроекПрав(Результат, Контекст);
	УдалитьПоляНеиспользуемыхВидовДоступа(Результат, Контекст);
	ЗаполнитьНаличиеОграниченияПоВидуДоступаПользователи(Результат, Контекст);
	ЗаполнитьНаличиеОграниченияЧтения(Результат, Контекст);
	
	Контекст.Вставить("ЕстьОграничениеПоПользователям", Результат.ЕстьОграничениеПоПользователям);
	
	Если Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей = Неопределено Тогда
		Результат.РассчитыватьПраваПользователей     = Результат.ЕстьОграничениеПоПользователям;
		Результат.ИспользуетсяОграничениеПоВладельцу = Результат.ПолеВладельца <> Неопределено
			И Не Результат.ПолеВладельца.Отключено;
		
		Контекст.Вставить("ИспользуетсяОграничениеПоВладельцу", Результат.ИспользуетсяОграничениеПоВладельцу);
		Контекст.Вставить("РассчитыватьПраваПользователей",     Результат.РассчитыватьПраваПользователей);
	КонецЕсли;
	ДобавитьСвойствоВерсии(Контекст, Контекст, "ИспользуетсяОграничениеПоВладельцу");
	ДобавитьСвойствоВерсии(Контекст, Контекст, "РассчитыватьПраваПользователей");
	ДобавитьСвойствоВерсии(Контекст, Контекст, "ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа");
	ДобавитьСвойствоВерсии(Контекст, Контекст, "ЧтениеРазрешеноДляВсехПользователей");
	ДобавитьСвойствоВерсии(Контекст, Контекст, "ИзменениеРазрешеноДляВсехПользователей");
	
	Контекст.Вставить("ГруппыПолей");
	Контекст.Вставить("ПсевдонимыТабличныхЧастейОбъекта");
	Контекст.Вставить("ГруппыДополнительныхТаблиц");
	Контекст.Вставить("КоличествоТабличныхЧастейКлюча");
	
	ЗаполнитьГруппыПолейИДополнительныхТаблиц(Контекст);
	
	Результат.СоставПолей = Контекст.СоставПолей;
	
	Если Результат.Контекст.СвойстваПолей.Количество() = 0
	   И (Не Результат.ЭтоСсылочныйТип
	      Или Не Результат.ЕстьФункцияПравоДоступаИлиРольДоступна) Тогда // Ограничение отключено.
		
		Если Результат.ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа Тогда
			НастроитьСозданиеКлючаДоступаДляЗависимыхСписковБезКлючей(Результат);
			Результат.Версия = "   ";
		Иначе
			Результат.Версия = " ";
		КонецЕсли;
		Возврат Результат;
	КонецЕсли;
	
	Результат.ОграничениеОтключено = Ложь;
	
	Контекст.Вставить("ИмяПрава", "Чтение");
	ЗаполнитьСтруктуруРасчетаПрава(Результат.СтруктураРасчетаПраваЧтение,
		Контекст.СтруктураОграничения.ОграничениеЧтения, Контекст);
	
	Контекст.Вставить("ИмяПрава", "Изменение");
	ЗаполнитьСтруктуруРасчетаПрава(Результат.СтруктураРасчетаПраваИзменение,
		Контекст.СтруктураОграничения.ОграничениеИзменения, Контекст);
	
	Контекст.Удалить("ИмяПрава");
	ДобавитьСвойствоВерсииВедущиеРоли(Контекст);
	
	Если Результат.ИспользуетсяОграничениеПоВладельцу Тогда
		Контекст.ВедущиеСпискиПоЗначениямПолей = ОписаниеВедущихСписковПоЗначениямПолей();
	КонецЕсли;
	ВедущиеСписки.ПоЗначениямПолей     = Контекст.ВедущиеСпискиПоЗначениямПолей.Поля;
	ВедущиеСписки.ПоКлючамДоступа      = Контекст.ВедущиеСпискиПоКлючамДоступа.Списки;
	ВедущиеСписки.ПоЗначениямСГруппами = Контекст.ВедущиеСпискиПоЗначениямСГруппами.Списки;
	
	ХешированиеДанных = Новый ХешированиеДанных(ХешФункция.SHA256);
	СтрокаСвойствВерсии = СтрСоединить(Контекст.СвойстваВерсии, Символы.ПС);
	Контекст.СтрокаСвойствВерсии = СтрокаСвойствВерсии;
	ХешированиеДанных.Добавить(СтрокаСвойствВерсии);
	Результат.Версия = Base64Строка(ХешированиеДанных.ХешСумма);
	
	Возврат Результат;
	
КонецФункции

// Списки с полями от которых зависит ограничение доступа.
//
// Возвращаемое значение:
//   Структура:
//     * ПоЗначениямПолей     - Соответствие - списки с полями, от которых зависит ограничение доступа
//                                             (для регистрации заданий обновления).
//     * ПоКлючамДоступа      - Соответствие - списки от ключей доступа которых зависит ограничение доступа
//                                             (для установки параметров сеанса и регистрации заданий обновления).
//     * ПоЗначениямСГруппами - Соответствие - списки значений доступа с группами, от которых зависит
//                                             ограничение доступа (для регистрации заданий обновления).
//
Функция НовыеВедущиеСписки()
	
	ВедущиеСписки = Новый Структура;
	ВедущиеСписки.Вставить("ПоЗначениямПолей",     Новый Соответствие);
	ВедущиеСписки.Вставить("ПоКлючамДоступа",      Новый Соответствие);
	ВедущиеСписки.Вставить("ПоЗначениямСГруппами", Новый Соответствие);
	
	Возврат ВедущиеСписки;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * СтрокаСвойствВерсии     - Строка
//     * Список                  - Строка
//     * ДляВнешнихПользователей - Булево
//     * ЭтоСсылочныйТип         - Булево
//     * СписокСДатой            - Булево
//     * СписокСПериодом         - Булево
//     * ИмяКоллекцииТипа        - Строка
//     * СвойстваВерсии          - Массив из Строка
//     * ПоляТаблицОбъекта       - см. НовоеОписаниеПолейТаблицОбъекта
//     * ОпорныеПоля             - см. НовоеОписаниеОпорныхПолей
//     * ВариантДоступа          - Число - основной вариант доступа смотри также НовыеОсновныеВариантыДоступа.
//     * ИспользуетсяОграничениеПоВладельцу        - Булево
//     * РассчитыватьПраваПользователей            - Булево
//     * ЕстьЗависимыеСпискиБезЗаписиКлючейДоступа - Булево
//     * ЧтениеРазрешеноДляВсехПользователей       - Булево
//     * ИзменениеРазрешеноДляВсехПользователей    - Булево
//     * БезОбъектаМетаданных                      - Булево
//     * СтруктураОграничения                      - см. СтруктураОграничения
//     * ИсходнаяСтруктураОграничения              - см. СтруктураОграничения
//     * ВедущиеСпискиПоЗначениямПолей             - см. ОписаниеВедущихСписковПоЗначениямПолей
//     * ВедущиеСпискиПоКлючамДоступа              - см. ОписаниеВедущихСписковПоПолюСсылка
//     * ВедущиеСпискиПоЗначениямСГруппами         - см. ОписаниеВедущихСписковПоПолюСсылка
//     * ЕстьПроверкаАвторизованногоПользователя   - Булево
//     * ЕстьФункцияПравоДоступаИлиРольДоступна    - Булево
//     * ЕстьФункцияПравоДоступаИлиРольДоступнаВОграниченииЧтения - Булево
//     * НеиспользуемыеТипыЗначенийДоступа         - Массив из Тип
//     * ВсеВидыОграниченийПрав                    - Соответствие
//     * ЕстьОграничениеПоПользователям            - Булево
//     * ИспользуетсяОграничениеПоВладельцу        - Булево
//     * РассчитыватьПраваПользователей            - Булево
//     * СвойстваВсехПолей - Соответствие из КлючИЗначение:
//         ** Ключ     - см. ОписаниеУзла
//         ** Значение - см. СвойстваПоля
//     * ОставшиесяПоляПослеУпрощения - Соответствие из КлючИЗначение:
//         ** Ключ     - см. ОписаниеУзла
//         ** Значение - Массив из см. ОписаниеУзла
//     * ПоляКлючаДоступаПослеУпрощения - Массив из см. НовоеПолеКлючаДоступа
//     * СвойстваПолейКлючаДоступа - Соответствие из КлючИЗначение:
//         ** Ключ     - см. ОписаниеУзла
//         ** Значение - см. СвойстваПоля
//     * ГруппыПолей - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - имя группы полей (Шапка?, ТабличнаяЧасть?)
//         ** Значение - Массив из см. СвойстваПоля
//     * ПсевдонимыТабличныхЧастейОбъекта - Соответствие из КлючИЗначение:
//         ** Ключ     - Число  - номер табличной части ключа
//         ** Значение - Строка - псевдоним таблицы
//     * ГруппыДополнительныхТаблиц - см. ГруппыДополнительныхТаблиц
//     * КоличествоТабличныхЧастейКлюча - Число
//     * ПоляУсловияСоединенияДополнительныхТаблиц - Массив из Структура:
//         ** УзелПоле - см. ОписаниеУзла
//         ** ПсевдонимТаблицыУсловия - Строка
//     * ИмяПрава - Строка
//     * ТребуемыеРеквизитыТабличныхЧастейКлюча - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - имя таблицы ключа (ИмяГруппыПолейКлючаДоступа)
//         ** Значение - Массив из Строка - имя реквизита таблицы ключа (ИмяРеквизитаГруппыПолейКлючаДоступа)
//     * СтруктураРасчетаПраваСвойстваВерсии - Массив из Строка
//     * ВедущиеРоли - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - имя роли
//         ** Значение - Булево - значение Истина.
//     
//   Свойства, скопированные от ОбщийКонтекст:
//     * СвойстваВидовДоступа         - см. СвойстваВидовДоступа
//     * ТипыПользователя             - Массив из Тип
//     * ТипыВладельцевНастроекПрав   - ФиксированноеСоответствие
//     * ОтдельныеТаблицыНастроекПрав - ФиксированноеСоответствие
//     * ИспользуемыеТипыЗначений     - см. ИспользуемыеТипыЗначений
//     * СпискиСОграничением          - см. УправлениеДоступомСлужебныйПовтИсп.СпискиСОграничением
//     * ВнешниеПользователиВключены  - Булево
//      
//   Свойства, скопированные от ДополнительныйКонтекст:
//     * ОписанияОграничений - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - см. СокращенноеОписаниеОграничения
//     * СвойстваОграниченияСписков - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - см. НовыеСвойстваОграниченияСписка
//     * СпискиСОграничениемПоВладельцу - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - значение ПоВладельцу, кроме Неопределено.
//     * СпискиСОтключеннымОграничением - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * СпискиСОтключеннымОграничениемЧтения - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//     * СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - полное имя списка
//         ** Значение - Булево - Истина
//
Функция КонтекстПараметровПоСтруктуреОграничения()
	
	Возврат Новый Структура;
	
КонецФункции

// Структура ограничения, приведенную к формату размещения значений в ключах доступа.
//
// Возвращаемое значение:
//   Структура:
//     * Узел - Строка - одна из строк "Поле", "Константа", "И", "Или", "Не", "Выбор",
//         "ЗначениеРазрешено",      "ЭтоАвторизованныйПользователь",
//         "ЧтениеОбъектаРазрешено", "ИзменениеОбъектаРазрешено",
//         "ЧтениеСпискаРазрешено",  "ИзменениеСпискаРазрешено",
//         "ДляВсехСтрок",           "ДляОднойИзСтрок".
//
//    Свойства узла Поле.
//     * Таблица   - Строка - таблица ключа доступа (Шапка?, ТабличнаяЧасть?).
//     * Реквизит  - Строка - имя реквизита таблицы ключа доступа (Реквизит?).
//     * ПроверкаЕстьNull - Булево - Истина (необязательное свойство).
//
//    Свойства узла Константа.
//     * Значение - Булево
//                - Число
//                - Строка
//                - Неопределено - Ложь, Истина, произвольное
//                     целое число до 16 разрядов или произвольная строка до 150 символов.
//
//    Свойства узлов И, Или.
//     * Аргументы - Массив из см. СтруктураРасчетаПрава
//
//    Свойства узла Не.
//     * Аргумент - см. СтруктураРасчетаПрава
//
//     * Узел - Строка - заглушка к предыдущей строке (для определения типа в EDT)
//
//    Свойства узла Выбор (Иначе может быть Неопределено).
//     * Иначе - см. СтруктураРасчетаПрава
//     * Когда - Массив из Структура:
//         ** Условие  - см. СтруктураРасчетаПрава
//         ** Значение - см. СтруктураРасчетаПрава
//      
//     * Узел - Строка - заглушка к предыдущей строке (для определения типа в EDT)
//     
//    Свойства узлов ЗначениеРазрешено,      ЭтоАвторизованныйПользователь,
//                   ЧтениеОбъектаРазрешено, ИзменениеОбъектаРазрешено,
//                   ЧтениеСпискаРазрешено,  ИзменениеСпискаРазрешено.
//     * Поле - см. СтруктураРасчетаПрава
//     * УточненияСравнения - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка
//                     - Тип - уточняемое значение "Неопределено", "Null", "ПустаяСсылка",
//                             "Отключено", Тип (Ссылка, Число, Даты, Булево).
//         ** Значение - Строка - результат "Ложь", "Истина".
//
//    Свойства узлов ДляВсехСтрок, ДляОднойИзСтрок.
//     * Аргумент - см. СтруктураРасчетаПрава
//
Функция СтруктураРасчетаПрава()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедуры ПараметрыОграниченияПоСтруктуреОграничения.
Процедура НастроитьСозданиеКлючаДоступаДляЗависимыхСписковБезКлючей(Результат)
	
	Если Не Результат.ЭтоСсылочныйТип Тогда
		Возврат;
	КонецЕсли;
	
	Если Результат.СоставПолей <> 0 Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для списка %1 некорректно рассчиталось поле %2.
				|Указано значение %3, ожидалось значение 0.'"),
			Результат.Список, "СоставПолей", Формат(Результат.СоставПолей, "ЧН=0; ЧГ="));
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Результат.ЕстьОграничениеЧтения = Истина;
	Результат.ЕстьОграничениеИзменения = Истина;
	
	Результат.ЕстьФункцияПравоДоступаИлиРольДоступна = Истина;
	
	СтруктураРасчетаПраваЧтение = Новый Структура;
	СтруктураРасчетаПраваЧтение.Вставить("Узел", "ПравоДоступа");
	СтруктураРасчетаПраваЧтение.Вставить("ИмяПрава", "Чтение");
	СтруктураРасчетаПраваЧтение.Вставить("ПолноеИмяОбъектаМетаданных", Результат.Список);
	СтруктураРасчетаПраваЧтение.Вставить("ТребуемыеРеквизитыТабличныхЧастейКлюча", Новый Соответствие);
	Результат.СтруктураРасчетаПраваЧтение = СтруктураРасчетаПраваЧтение;
	
	СтруктураРасчетаПраваИзменение = Новый Структура;
	СтруктураРасчетаПраваИзменение.Вставить("Узел", "ПравоДоступа");
	СтруктураРасчетаПраваИзменение.Вставить("ИмяПрава", "Изменение");
	СтруктураРасчетаПраваИзменение.Вставить("ПолноеИмяОбъектаМетаданных", Результат.Список);
	СтруктураРасчетаПраваИзменение.Вставить("ТребуемыеРеквизитыТабличныхЧастейКлюча", Новый Соответствие);
	Результат.СтруктураРасчетаПраваИзменение = СтруктураРасчетаПраваИзменение;
	
КонецПроцедуры

// Для процедуры ПараметрыОграниченияПоСтруктуреОграничения.
Функция ПравоРазрешеноДляВсехПользователей(ИмяПрава, ОбъектМетаданных, ДляВнешнихПользователей)
	
	Если ОбъектМетаданных = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ИменаРолейБазовыеПрава = УправлениеДоступомСлужебныйПовтИсп.ИменаРолейБазовыеПрава(ДляВнешнихПользователей);
	МетаданныеРоли = Метаданные.Роли;
	
	Для Каждого ИмяРоли Из ИменаРолейБазовыеПрава Цикл
		Если ПравоДоступа(ИмяПрава, ОбъектМетаданных, МетаданныеРоли[ИмяРоли]) Тогда
			Возврат Истина;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

// Для процедуры ПараметрыОграниченияПоСтруктуреОграничения.
Процедура ДобавитьСвойствоВерсииВедущиеРоли(Контекст)
	
	Если Не ЗначениеЗаполнено(Контекст.ВедущиеРоли) Тогда
		Возврат;
	КонецЕсли;
	
	СписокЗначений = Новый СписокЗначений;
	Для Каждого КлючИЗначение Из Контекст.ВедущиеРоли Цикл
		СписокЗначений.Добавить(КлючИЗначение.Ключ);
	КонецЦикла;
	СписокЗначений.СортироватьПоПредставлению();
	
	ДобавитьЭлементВерсии(Контекст, "ВедущиеРоли", СписокЗначений.ВыгрузитьЗначения());
	
КонецПроцедуры

// Для процедуры УдалитьПоляНеиспользуемыхВидовДоступа и функции ГруппыДополнительныхТаблиц.
Процедура ДобавитьСвойстваВерсии(Контекст, Структура, ИменаПолей = "") Экспорт
	
	Если ЗначениеЗаполнено(ИменаПолей) Тогда
		СохраняемаяСтруктура = Новый Структура(ИменаПолей);
		ЗаполнитьЗначенияСвойств(СохраняемаяСтруктура, Структура);
	Иначе
		СохраняемаяСтруктура = Структура;
	КонецЕсли;
	
	СписокЗначений = Новый СписокЗначений;
	Для Каждого КлючИЗначение Из СохраняемаяСтруктура Цикл
		СписокЗначений.Добавить(КлючИЗначение.Значение, КлючИЗначение.Ключ)
	КонецЦикла;
	СписокЗначений.СортироватьПоПредставлению();
	
	Для Каждого ЭлементСписка Из СписокЗначений Цикл
		ДобавитьЭлементВерсии(Контекст, ЭлементСписка.Представление, ЭлементСписка.Значение);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ПараметрыОграниченияПоСтруктуреОграничения.
Процедура ДобавитьСвойствоВерсии(Контекст, Структура, ИмяПоля)
	
	ДобавитьЭлементВерсии(Контекст, ИмяПоля, Структура[ИмяПоля]);
	
КонецПроцедуры

// Для процедур ДобавитьСвойстваВерсии, ДобавитьСвойствоВерсии.
Процедура ДобавитьЭлементВерсии(Контекст, ИмяПоля, Значение) Экспорт
	
	Если ТипЗнч(Значение) = Тип("Строка") Тогда
		Контекст.СвойстваВерсии.Добавить(ИмяПоля + " = " + Значение);
		
	ИначеЕсли ТипЗнч(Значение) = Тип("Число") Тогда
		
		Контекст.СвойстваВерсии.Добавить(ИмяПоля + " = " + Формат(Значение, "ЧГ="));
		
	ИначеЕсли ТипЗнч(Значение) = Тип("Булево") Тогда
		
		Контекст.СвойстваВерсии.Добавить(ИмяПоля + " = " + ?(Значение, "Да", "Нет"));
		
	ИначеЕсли ТипЗнч(Значение) = Тип("Неопределено") Тогда
		
		Контекст.СвойстваВерсии.Добавить(ИмяПоля + " = " + "Неопределено");
		
	ИначеЕсли ТипЗнч(Значение) = Тип("ФиксированныйМассив") Тогда
		Контекст.СвойстваВерсии.Добавить(ИмяПоля + " = " + СтрокаДанныхДляХеширования(Новый Массив(Значение)));
		
	ИначеЕсли ТипЗнч(Значение) = Тип("ОписаниеТипов")
	      Или ТипЗнч(Значение) = Тип("Тип")
	      Или ТипЗнч(Значение) = Тип("Массив") Тогда
		
		Контекст.СвойстваВерсии.Добавить(ИмяПоля + " = " + СтрокаДанныхДляХеширования(Значение));
	Иначе
		ТекстОшибки = НСтр("ru = 'Некорректный тип данных для версии ограничения доступа.'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

// Для функции ПараметрыОграниченияПоСтруктуреОграничения и формы ОбновлениеДоступаРучноеУправление.
//
// Возвращаемое значение:
//  Булево
//
Функция ЭтоСсылочныйТипТаблицы(ПолноеИмя, ИмяКоллекцииТипа = "") Экспорт
	
	СинтаксисЯзыка = УправлениеДоступомСлужебныйПовтИсп.СинтаксисЯзыка();
	СоставИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
	
	ТипТаблицы = СинтаксисЯзыка.ТипыТаблиц.ПоИменам.Получить(ВРег(СоставИмени[0]));
	Если ТипТаблицы = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ИмяКоллекцииТипа = ТипТаблицы.ИмяКоллекции;
	
	Возврат ТипТаблицы.ЭтоСсылочныйТип;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * ПолноеИмяТаблицы - Строка
//    * ТабличнаяЧасть   - Строка
//    * Поля             - Массив из Строка - с полем Ссылка
//    * СписокПолей      - Строка - без поля Ссылка
//    * ТаблицаСПолями   - ХранилищеЗначения - с объектом ТаблицаЗначений
//                         с типизированными полями (включая поле Ссылка).
//
Функция НовыеПоляТаблицыОбъекта()
	
	ПоляТаблицы = Новый Структура;
	ПоляТаблицы.Вставить("ПолноеИмяТаблицы", "");
	ПоляТаблицы.Вставить("ТабличнаяЧасть", "");
	ПоляТаблицы.Вставить("Поля", Новый Массив);
	ПоляТаблицы.Вставить("СписокПолей", "");
	ПоляТаблицы.Вставить("ТаблицаСПолями", Новый ХранилищеЗначения(Новый ТаблицаЗначений));
	
	Возврат ПоляТаблицы;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * Результат - Массив из см. НовыеПоляТаблицыОбъекта
//    * Состав - Структура:
//        ** Ключ - Строка - полное имя таблицы объекта.
//        ** Значение - Структура:
//             *** Ключ - Строка - имя поля.
//             *** Значение - Структура:
//                   **** Тип - ОписаниеТипов - типы поля.
//                   **** Использование - Булево - использование поля.
//    * ПоСвойствамПолей - Соответствие из КлючИЗначение:
//        ** Ключ     - см. НовыеСвойстваПоля
//        ** Значение - Структура:
//             *** Таблица - Строка - полное имя таблицы объекта.
//             *** Поле    - Строка - имя поля.
//    * ПоДополнительнымТаблицам - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - дополнительная таблица
//        ** Значение - Структура:
//             *** Ключ - Строка - полное имя таблицы объекта.
//             *** Значение - Массив из Строка - имя поля.
//
Функция НовоеОписаниеПолейТаблицОбъекта(Результат)
	
	Возврат Новый Структура("Результат, Состав", Результат.ПоляТаблицОбъекта, Новый Структура);
	
КонецФункции

// Для функции ПараметрыОграниченияПоСтруктуреОграничения.
//
// Параметры:
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьНовоеОписаниеОпорныхПолей(Результат, Контекст)
	
	ОпорныеПоля = НовоеОписаниеОпорныхПолей();
	Контекст.Вставить("ОпорныеПоля", ОпорныеПоля);
	Контекст.Вставить("ВариантДоступа", Неопределено);
	
	Если Контекст.ЭтоСсылочныйТип Тогда
		Возврат;
	КонецЕсли;
	
	Если Контекст.Свойство("ОсновныеВариантыДоступа") Тогда
		ИспользуемыеВариантыДоступа = Контекст.ОсновныеВариантыДоступа.Получить(Контекст.Список);
		Если ИспользуемыеВариантыДоступа <> Неопределено Тогда
			Результат.ВариантДоступа = ИспользуемыеВариантыДоступа[0].ВариантДоступа;
		КонецЕсли;
		Контекст.Вставить("ВариантДоступа", Результат.ВариантДоступа);
	КонецЕсли;
	
	Если СтрРазделить(Контекст.Список, ".").Количество() > 1 Тогда
		ИмяОтдельногоРегистраКлючей = "КлючиДоступаКРегистру" + СтрРазделить(Контекст.Список, ".")[1];
		Если Метаданные.РегистрыСведений.Найти(ИмяОтдельногоРегистраКлючей) = Неопределено Тогда
			ИмяОтдельногоРегистраКлючей = "";
		КонецЕсли;
	Иначе
		ИмяОтдельногоРегистраКлючей = "";
	КонецЕсли;
	Контекст.Вставить("ИмяОтдельногоРегистраКлючей", ИмяОтдельногоРегистраКлючей);
	
	ОпорныеПоля.МаксимальноеКоличество =
		УправлениеДоступомСлужебныйПовтИсп.КоличествоОпорныхПолейРегистра(ИмяОтдельногоРегистраКлючей);
	
	ОпорныеПоля.МаксимальноДопустимоеКоличество =
		УправлениеДоступомСлужебныйПовтИсп.МаксимальноеКоличествоОпорныхПолейРегистра();
	
	Если ОпорныеПоля.МаксимальноеКоличество > ОпорныеПоля.МаксимальноДопустимоеКоличество Тогда
		// Превышение количества опорных полей в отдельном регистре.
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Количество опорных полей в регистре сведений %1
			           |превышает максимально допустимое количество: %2'"),
			?(ИмяОтдельногоРегистраКлючей = "", "КлючиДоступаКРегистрам", ИмяОтдельногоРегистраКлючей),
			ОпорныеПоля.МаксимальноДопустимоеКоличество);
		
		Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
			Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
		КонецЕсли;
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ДобавитьСвойствоВерсии(Контекст, ОпорныеПоля, "МаксимальноеКоличество");
	
	Результат.ОпорныеПоля = Новый Структура("Все, ТипыВсех, Используемые, ТипыИспользуемых,
	|МаксимальноеКоличество, МаксимальноДопустимоеКоличество");
	ЗаполнитьЗначенияСвойств(Результат.ОпорныеПоля, Контекст.ОпорныеПоля);
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура:
//    * Список                          - СписокЗначений
//    * Все                             - Массив
//    * ТипыВсех                        - Массив из ХранилищеЗначения - содержит тип ОписаниеТипов
//    * Используемые                    - Массив
//    * ТипыИспользуемых                - Массив из ХранилищеЗначения - содержит тип ОписаниеТипов
//    * МаксимальноеКоличество          - Число
//    * МаксимальноДопустимоеКоличество - Число
//    * ТипыПоИменамПолей - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - имя опорного поля
//        ** Значение - ОписаниеТипов - типы опорного поля
//    * ПоСвойствамПолей - Соответствие из КлючИЗначение:
//        ** Ключ     - см. НовыеСвойстваПоля
//        ** Значение - Строка - имя опорного поля
//    * ПоДополнительнымТаблицам - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - дополнительная таблица
//        ** Значение - Массив из Строка - имена опорных полей
//
Функция НовоеОписаниеОпорныхПолей()
	
	ОпорныеПоля = Новый Структура;
	ОпорныеПоля.Вставить("Список",           Новый СписокЗначений);
	ОпорныеПоля.Вставить("Все",              Новый Массив);
	ОпорныеПоля.Вставить("ТипыВсех",         Новый Массив);
	ОпорныеПоля.Вставить("Используемые",     Новый Массив);
	ОпорныеПоля.Вставить("ТипыИспользуемых", Новый Массив);
	ОпорныеПоля.Вставить("МаксимальноеКоличество", 0);
	ОпорныеПоля.Вставить("МаксимальноДопустимоеКоличество", 0);
	
	Возврат ОпорныеПоля;
	
КонецФункции

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//
Функция ЭтоБезусловноеОграничение(Контекст, Результат)
	
	СтруктураОграничения = Контекст.СтруктураОграничения;
	
	Возврат ЗначениеЗаполнено(СтруктураОграничения.ОграничениеЧтения)
		И СтруктураОграничения.ОграничениеЧтения.Узел = "Константа"
		И СтруктураОграничения.ОграничениеЧтения.Значение = Ложь;
	
КонецФункции

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//
Процедура ЗаполнитьНаличиеОграниченияПоПравам(Контекст, Результат, ПослеУпрощения)
	
	СтруктураОграничения = Контекст.СтруктураОграничения;
	
	Результат.ЕстьОграничениеЧтения =
		ЗначениеЗаполнено(СтруктураОграничения.ОграничениеЧтения)
		И (    СтруктураОграничения.ОграничениеЧтения.Узел <> "Константа"
		   Или СтруктураОграничения.ОграничениеЧтения.Значение <> Истина);
	
	Результат.ЕстьОграничениеИзменения =
		ЗначениеЗаполнено(СтруктураОграничения.ОграничениеИзменения)
		И (    СтруктураОграничения.ОграничениеИзменения.Узел <> "Константа"
		   Или СтруктураОграничения.ОграничениеИзменения.Значение <> Истина);
	
	Если ПослеУпрощения Тогда
		ДобавитьСвойствоВерсии(Контекст, Результат, "ЕстьОграничениеЧтения");
		ДобавитьСвойствоВерсии(Контекст, Результат, "ЕстьОграничениеИзменения");
	КонецЕсли;
	
КонецПроцедуры

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьОграничениеПоОбъектуВладельцуДоУпрощения(Результат, Контекст)
	
	НастройкаПоВладельцу = Контекст.СпискиСОграничениемПоВладельцу.Получить(Контекст.Список);
	ПолеВладельца = НовоеПолеВладельца();
	
	ТребуетсяОграничениеПоВладельцу = Ложь;
	Если ТипЗнч(НастройкаПоВладельцу) = Тип("Булево") Тогда
		ТребуетсяОграничениеПоВладельцу = НастройкаПоВладельцу;
	КонецЕсли;
	Результат.ТребуетсяОграничениеПоВладельцу = ТребуетсяОграничениеПоВладельцу;
	
	ОграничениеЧтения    = Контекст.СтруктураОграничения.ОграничениеЧтения;
	ОграничениеИзменения = Контекст.СтруктураОграничения.ОграничениеИзменения;
	
	ИмяПризнакаОптимизации = ?(Контекст.ДляВнешнихПользователей,
		"ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей", "ПоВладельцуБезЗаписиКлючейДоступа");
	
	Если Результат.ЕстьОграничениеЧтения Тогда
		
		Если ТребуетсяОграничениеПоВладельцу Или Не Контекст.ЭтоСсылочныйТип Тогда
			Если ОграничениеЧтения.Узел <> "ЧтениеОбъектаРазрешено" Тогда
				Если Не ТребуетсяОграничениеПоВладельцу Тогда
					Возврат;
				КонецЕсли;
				Если ЗначениеЗаполнено(ОграничениеИзменения) Тогда
					ШаблонОшибки =
						НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
						           |но указанное ограничение чтения изменения не представлено одной функцией ""%2"".'");
				Иначе
					ШаблонОшибки =
						НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
						           |но указанное ограничение чтения не представлено одной функцией ""%2"".'");
				КонецЕсли;
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
					ИмяПризнакаОптимизации, "ЧтениеОбъектаРазрешено");
				ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
				Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
					Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
				КонецЕсли;
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		Иначе
			ОграничениеЧтения = ВозможноеОграничениеПоОбъектуВладельцу(ОграничениеЧтения, Ложь, Контекст);
			Если ОграничениеЧтения = Неопределено Тогда
				Возврат;
			КонецЕсли;
		КонецЕсли;
		Если ЗначениеЗаполнено(ОграничениеЧтения) Тогда
			Если Не ФункцияБезУточненийТиповСПолемБезВложений(ОграничениеЧтения) Тогда
				Если ТребуетсяОграничениеПоВладельцу Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
						           |но параметры функции ""%2"" не представлены
						           |только одним параметром - полем владельцем без других параметров.'"),
						ИмяПризнакаОптимизации,
						"ЧтениеОбъектаРазрешено");
					ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
					Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
						Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
					КонецЕсли;
					ВызватьИсключение ТекстОшибки;
				Иначе
					Возврат;
				КонецЕсли;
			КонецЕсли;
			ОграничениеЧтенияПоле = ОграничениеЧтения.Поле; // См. ОписаниеУзла
			ПолеВладельца.Имя = ОграничениеЧтенияПоле.Имя;
		КонецЕсли;
	КонецЕсли;
	
	Если Результат.ЕстьОграничениеИзменения Тогда
		
		Если ТребуетсяОграничениеПоВладельцу Или Не Контекст.ЭтоСсылочныйТип Тогда
			
			Если ОграничениеИзменения.Узел <> "ЧтениеОбъектаРазрешено"
			   И ОграничениеИзменения.Узел <> "ИзменениеОбъектаРазрешено" Тогда
				
				Если Не ТребуетсяОграничениеПоВладельцу Тогда
					Возврат;
				КонецЕсли;
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
					           |но указанное ограничение изменения не представлено одной функцией
					           |""%2"" или ""%3"".'"),
					ИмяПризнакаОптимизации, "ЧтениеОбъектаРазрешено", "ИзменениеОбъектаРазрешено");
				ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
				Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
					Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
				КонецЕсли;
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		Иначе
			ОграничениеИзменения = ВозможноеОграничениеПоОбъектуВладельцу(ОграничениеИзменения, Истина, Контекст);
			Если ОграничениеИзменения = Неопределено Тогда
				Возврат;
			КонецЕсли;
		КонецЕсли;
		Если ЗначениеЗаполнено(ОграничениеИзменения) Тогда
			Если Не ФункцияБезУточненийТиповСПолемБезВложений(ОграничениеИзменения) Тогда
				Если ТребуетсяОграничениеПоВладельцу Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
						           |но параметры функции ""%2"" не представлены
						           |только одним параметром - полем владельцем без других параметров.'"),
						ИмяПризнакаОптимизации,
						ОграничениеИзменения.Узел);
					ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
					Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
						Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
					КонецЕсли;
					ВызватьИсключение ТекстОшибки;
				Иначе
					Возврат;
				КонецЕсли;
			КонецЕсли;
			ОграничениеИзмененияПоле = ОграничениеИзменения.Поле; // См. ОписаниеУзла
			
			Если ПолеВладельца.Имя = "" Тогда
				ПолеВладельца.Имя = ОграничениеИзмененияПоле.Имя;
				
			ИначеЕсли ПолеВладельца.Имя <> ОграничениеИзмененияПоле.Имя Тогда
				Если ТребуетсяОграничениеПоВладельцу Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
						           |но поле владельца не совпадает в ограничениях чтения и изменения.'"),
						ИмяПризнакаОптимизации,
						ОграничениеИзменения.Узел);
					ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
					Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
						Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
					КонецЕсли;
					ВызватьИсключение ТекстОшибки;
				Иначе
					Возврат;
				КонецЕсли;
			КонецЕсли;
			ПолеВладельца.ИзменениеКакЧтение =
				ОграничениеИзменения.Узел <> "ИзменениеОбъектаРазрешено";
		Иначе
			ПолеВладельца.ИзменениеКакЧтение = Истина;
		КонецЕсли;
	Иначе
		ПолеВладельца.ИзменениеКакЧтение = Истина;
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ПолеВладельца.Имя) Тогда
		Если ТребуетсяОграничениеПоВладельцу Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
				           |но указанное ограничение не содержит ни одной из функций
				           |""%2"", ""%3"".'"),
				ИмяПризнакаОптимизации, "ЧтениеОбъектаРазрешено", "ИзменениеОбъектаРазрешено");
			ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
			Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
				Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
			КонецЕсли;
			ВызватьИсключение ТекстОшибки;
		Иначе
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Результат.ПолеВладельца = ПолеВладельца;
	
	Если Не Контекст.ЭтоСсылочныйТип Тогда
		Результат.ТребуетсяОграничениеПоВладельцу = Истина;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьОграничениеПоОбъектуВладельцуДоУпрощения.
Функция ВозможноеОграничениеПоОбъектуВладельцу(Условие, ЭтоОграничениеИзменения, Контекст)
	
	Если Не ЗначениеЗаполнено(Условие) Тогда
		Возврат Новый Структура;
	КонецЕсли;
	
	Если Условие.Узел = "Поле"
	 Или Условие.Узел = "ЕстьNull"
	 Или Условие.Узел = "="
	 Или Условие.Узел = "<>"
	 Или Условие.Узел = "В"
	 Или Условие.Узел = "Константа"
	 Или Условие.Узел = "ЗначениеРазрешено"  Тогда
		
		Возврат Новый Структура;
		
	ИначеЕсли Условие.Узел = "ЭтоАвторизованныйПользователь"
	      Или Условие.Узел = "ЧтениеСпискаРазрешено"
	      Или Условие.Узел = "ИзменениеСпискаРазрешено"
	      Или Условие.Узел = "ПравоДоступа"
	      Или Условие.Узел = "РольДоступна" Тогда
		
		Возврат Неопределено;
		
	ИначеЕсли Условие.Узел = "И"
	      Или Условие.Узел = "Или" Тогда
		
		НайденноеОграничение = Новый Структура;
		Для Каждого Аргумент Из Условие.Аргументы Цикл
			Если Не ОбработаноВозможноеОграничениеПоОбъектуВладельцу(Аргумент,
						ЭтоОграничениеИзменения, НайденноеОграничение, Контекст) Тогда
				Возврат Неопределено;
			КонецЕсли;
		КонецЦикла;
		Возврат НайденноеОграничение;
		
	ИначеЕсли Условие.Узел = "Не"
	      Или Условие.Узел = "ДляВсехСтрок"
	      Или Условие.Узел = "ДляОднойИзСтрок" Тогда
		
		Возврат ВозможноеОграничениеПоОбъектуВладельцу(Условие.Аргумент,
			ЭтоОграничениеИзменения, Контекст);
		
	ИначеЕсли Условие.Узел = "Выбор" Тогда
		НайденноеОграничение = Новый Структура;
		Для Каждого Когда Из Условие.Когда Цикл
			Если Не ОбработаноВозможноеОграничениеПоОбъектуВладельцу(Когда.Значение,
						ЭтоОграничениеИзменения, НайденноеОграничение, Контекст) Тогда
				Возврат Неопределено;
			КонецЕсли;
		КонецЦикла;
		Если Не ОбработаноВозможноеОграничениеПоОбъектуВладельцу(Условие.Иначе,
					ЭтоОграничениеИзменения, НайденноеОграничение, Контекст) Тогда
			Возврат Неопределено;
		КонецЕсли;
		Возврат НайденноеОграничение;
		
	ИначеЕсли Условие.Узел = "ЧтениеОбъектаРазрешено"
	      Или Условие.Узел = "ИзменениеОбъектаРазрешено" И ЭтоОграничениеИзменения Тогда
		
		Возврат Условие;
	КонецЕсли;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'При заполнении возможного ограничения по объекту владельцу
		           |для списка ""%1""
		           |узел не поддерживается ""%2"".'"),
		Контекст.Список,
		Условие.Узел);
	
	ВызватьИсключение ТекстОшибки;
	
КонецФункции

// Для функции ВозможноеОграничениеПоОбъектуВладельцу.
Функция ОбработаноВозможноеОграничениеПоОбъектуВладельцу(Условие,
				ЭтоОграничениеИзменения, НайденноеОграничение, Контекст)
	
	ВложенноеОграничение = ВозможноеОграничениеПоОбъектуВладельцу(Условие,
		ЭтоОграничениеИзменения, Контекст);
	
	Если ВложенноеОграничение = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ВложенноеОграничение) Тогда
		Если ЗначениеЗаполнено(НайденноеОграничение) Тогда
			Возврат Ложь;
		КонецЕсли;
		НайденноеОграничение = ВложенноеОграничение;
	КонецЕсли;
	
	Возврат Истина;
	
КонецФункции

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьОграничениеПоОбъектуВладельцуПослеУпрощения(Результат, Контекст)
	
	ПолеВладельца = Результат.ПолеВладельца;
	Если ПолеВладельца = Неопределено Тогда
		Возврат;
	КонецЕсли;
	ТребуетсяОграничениеПоВладельцу = Результат.ТребуетсяОграничениеПоВладельцу;
	
	НастройкаПоВладельцу = Контекст.СпискиСОграничениемПоВладельцу.Получить(Контекст.Список);
	Если ТипЗнч(НастройкаПоВладельцу) = Тип("Булево") Тогда
		ПолеВладельца.Отключено = Не НастройкаПоВладельцу;
	Иначе
		ПолеВладельца.Отключено = Ложь;
	КонецЕсли;
	
	ОграничениеЧтения    = Контекст.СтруктураОграничения.ОграничениеЧтения;
	ОграничениеИзменения = Контекст.СтруктураОграничения.ОграничениеИзменения;
	
	ИмяПризнакаОптимизации = ?(Контекст.ДляВнешнихПользователей,
		"ПоВладельцуБезЗаписиКлючейДоступаДляВнешнихПользователей", "ПоВладельцуБезЗаписиКлючейДоступа");
	
	Если Не Результат.ЕстьОграничениеЧтения
	   И Не Результат.ЕстьОграничениеИзменения
	 Или Результат.ЕстьОграничениеЧтения
	   И ОграничениеЧтения.Узел <> "ЧтениеОбъектаРазрешено"
	 Или Результат.ЕстьОграничениеИзменения
	   И ОграничениеИзменения.Узел <> "ЧтениеОбъектаРазрешено"
	   И ОграничениеИзменения.Узел <> "ИзменениеОбъектаРазрешено" Тогда
		
		ПолеВладельца.Отключено = Истина;
	Иначе
		СвойстваПолей = Контекст.СвойстваПолей;
		
		Если СвойстваПолей.Количество() <> 1 Тогда
			Если ТребуетсяОграничениеПоВладельцу И СвойстваПолей.Количество() <> 0 Тогда
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
					           |но количество полей, используемых в ограничении, не равно одному.'"),
					ИмяПризнакаОптимизации);
				ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
				Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
					Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
				КонецЕсли;
				ВызватьИсключение ТекстОшибки;
			Иначе
				ПолеВладельца.Отключено = Истина;
			КонецЕсли;
		Иначе
			СвойстваПоля = СвойстваПолей[0];
			
			ТипИОМ = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных");
			ТипИОР = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений");
			
			КоличествоТиповИдентификаторовВКонечномПоле = 0;
			Если СвойстваПоля.ТипКонечногоПоля.СодержитТип(ТипИОМ) Тогда
				КоличествоТиповИдентификаторовВКонечномПоле = 1;
			КонецЕсли;
			Если СвойстваПоля.ТипКонечногоПоля.СодержитТип(ТипИОР) Тогда
				КоличествоТиповИдентификаторовВКонечномПоле = КоличествоТиповИдентификаторовВКонечномПоле + 1;
			КонецЕсли;
			
			Если СвойстваПоля.ТипКонечногоПоля.Типы().Количество() - КоличествоТиповИдентификаторовВКонечномПоле
			  <> СвойстваПоля.ТипыСохраненияКлючейДоступа.Количество() Тогда
				
				НекорректныеТипы = Новый Массив;
				Для Каждого Тип Из СвойстваПоля.ТипКонечногоПоля.Типы() Цикл
					Если СвойстваПоля.ТипыСохраненияКлючейДоступа.Найти(Тип) <> Неопределено
					 Или Тип = ТипИОМ
					 Или Тип = ТипИОР Тогда
						Продолжить;
					КонецЕсли;
					НекорректныеТипы.Добавить(ИмяТипаНаЯзыкеЗапросов(Тип));
				КонецЦикла;
				Если ТребуетсяОграничениеПоВладельцу Тогда
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Установлен признак оптимизации ограничения ""%1"",
						           |но для следующих таблиц невозможно записать ключи доступа:
						           |%2'"),
						ИмяПризнакаОптимизации,
						СтрСоединить(НекорректныеТипы, Символы.ПС));
					ТекстОшибки = ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст);
					Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
						Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
					КонецЕсли;
					ВызватьИсключение ТекстОшибки;
				Иначе
					ПолеВладельца.Отключено = Истина;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	ДобавитьСвойствоВерсии(Контекст, ПолеВладельца, "ИзменениеКакЧтение");
	ДобавитьСвойствоВерсии(Контекст, ПолеВладельца, "Имя");
	ДобавитьСвойствоВерсии(Контекст, ПолеВладельца, "Отключено");
	
КонецПроцедуры

// Возвращаемое значение:
//   Структура:
//     * Имя - Строка
//     * ИзменениеКакЧтение - Булево
//     * Отключено - Булево
//
Функция НовоеПолеВладельца()
	
	Возврат Новый Структура("Имя, ИзменениеКакЧтение, Отключено", "", Ложь, Истина);
	
КонецФункции

// Для функции ЗаполнитьОграничениеПоОбъектуВладельцу и процедуры НастроитьОптимизациюПоПолюВладельцу.
Функция ТекстОшибкиСЗаголовком(ТекстОшибки, Контекст)
	
	ОписаниеОшибок = ОписаниеОшибок();
	ОписаниеОшибок.ЕстьОшибки = Истина;
	ОписаниеОшибок.ТекстОшибок = ТекстОшибки;
	ОписаниеОшибок.Ошибки = Новый Массив(1);
	
	ОписаниеОграничения = Контекст.ОписанияОграничений.Получить(Контекст.Список);
	
	ОписаниеОшибок.Ограничение =
		ПронумерованныйТекстОграниченияСОтметкамиОшибок(ОписаниеОграничения.Текст,
			Новый Массив, СтрДлина(Формат(СтрЧислоСтрок(ОписаниеОграничения.Текст), "ЧГ=")));
	
	Возврат СокрЛП(ТекстОшибокДляВызоваИсключения(Контекст.Список,
		ОписаниеОшибок, Контекст.ДляВнешнихПользователей, ОписаниеОграничения.ВМодулеМенеджера));
	
КонецФункции

// Для процедуры ЗаполнитьОграничениеПоОбъектуВладельцу.
Функция ФункцияБезУточненийТиповСПолемБезВложений(Ограничение)
	
	Если Ограничение.Типы.Количество() <> 0
	 Или Ограничение.УточненияСравнения.Количество() <> 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Поле = Ограничение.Поле;
	
	Возврат Не ЗначениеЗаполнено(Поле.Выразить)
	      И Не ЗначениеЗаполнено(Поле.Вложение)
	      И Не ЗначениеЗаполнено(Поле.ЕстьNull);
	
КонецФункции

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьНаличиеВедущихКлючейИСписковИВладельцевНастроекПрав(Результат, Контекст)
	
	Для Каждого СвойстваПоля Из Контекст.СвойстваПолей Цикл
		Если СвойстваПоля.ТипыСохраненияКлючейДоступа.Количество() > 0 Тогда
			Результат.ЕстьВедущиеКлючиДоступа = Истина;
		КонецЕсли;
		Если СвойстваПоля.ЕстьТипВедущегоСписка Тогда
			Результат.ЕстьВедущиеСпискиПоПравам = Истина;
		КонецЕсли;
		Если СвойстваПоля.ЕстьТипВладельцаНастроекПрав Тогда
			Результат.ЕстьВладельцыНастроекПрав = Истина;
		КонецЕсли;
		Если СвойстваПоля.ЕстьПроверкаАвторизованногоПользователя Тогда
			Контекст.ЕстьПроверкаАвторизованногоПользователя = Истина;
		КонецЕсли;
		Для Каждого Тип Из СвойстваПоля.ИспользуемыеТипыЗначенийДоступа Цикл
			Если Результат.ИспользуемыеТипыЗначенийДоступа.Найти(Тип) = Неопределено Тогда
				Результат.ИспользуемыеТипыЗначенийДоступа.Добавить(Тип);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Для Каждого КлючИЗначение Из Контекст.ОтдельныеТаблицыНастроекПрав Цикл
		Если КлючИЗначение.Значение = Контекст.Список Тогда
			Результат.ИдентификаторТаблицыНастроекПрав = КлючИЗначение.Ключ;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	ДобавитьСвойствоВерсии(Контекст, Результат, "ЕстьВедущиеКлючиДоступа");
	ДобавитьСвойствоВерсии(Контекст, Результат, "ЕстьВедущиеСпискиПоПравам");
	ДобавитьСвойствоВерсии(Контекст, Результат, "ЕстьВладельцыНастроекПрав");
	ДобавитьСвойствоВерсии(Контекст, Результат, "ИспользуемыеТипыЗначенийДоступа");
	ДобавитьСвойствоВерсии(Контекст, Контекст,  "НеиспользуемыеТипыЗначенийДоступа");
	
	СохраняемыеСвойства = Новый Структура("ОтдельнаяТаблицаНастроекПрав",
		ЗначениеЗаполнено(Результат.ИдентификаторТаблицыНастроекПрав));
	
	ДобавитьСвойствоВерсии(Контекст, СохраняемыеСвойства, "ОтдельнаяТаблицаНастроекПрав");
	
КонецПроцедуры

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура УдалитьПоляНеиспользуемыхВидовДоступа(Результат, Контекст)
	
	СвойстваПолей = Контекст.СвойстваПолей;
	
	Если Не Результат.ЕстьВедущиеКлючиДоступа
	   И Не Результат.ЕстьВедущиеСпискиПоПравам
	   И Не Результат.ЕстьВладельцыНастроекПрав
	   И Не Результат.ЕстьФункцияПравоДоступаИлиРольДоступна
	   И Не Контекст.ЕстьПроверкаАвторизованногоПользователя
	   И Результат.ИспользуемыеТипыЗначенийДоступа.Количество() = 0
	   И Контекст.НеиспользуемыеТипыЗначенийДоступа.Количество() > 0 Тогда
		
		СвойстваПолей.Очистить();
		Возврат;
	КонецЕсли;
	
	Индекс = СвойстваПолей.Количество();
	Пока Индекс > 0 Цикл
		Индекс = Индекс - 1;
		СвойстваПоля = СвойстваПолей.Получить(Индекс);
		УстановитьИспользованиеПоляТаблицыОбъекта(Контекст, СвойстваПоля);
		УстановитьИспользованиеОпорногоПоля(Контекст, СвойстваПоля);
		ДобавитьСвойстваВерсии(Контекст, СвойстваПоля,
		"ЕстьУточнениеNull,
		|ЕстьУточнениеНеопределено,
		|ИмяПоляДляЗапроса,
		|НесколькоГруппЗначений,
		|ТипКонечногоПоля,
		|ТипыСохраненияГруппЗначений,
		|ТипыСохраненияЗначений,
		|ТипыСохраненияКлючейДоступа,
		|ТипыСохраненияПустойСсылки,
		|ТипыСохраненияТипаРазрешенный,
		|ТипыСохраненияТипаЗапрещенный,
		|ТипыСохраненияТипов,
		|ТипыСохраненияТиповКонфигурации,
		|ТипыСохраненияТиповПростых,
		|ТипыСохраненияТиповРасширений,
		|ТипыСтрокой");
	КонецЦикла;
	
КонецПроцедуры

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьНаличиеОграниченияПоВидуДоступаПользователи(Результат, Контекст)
	
	Если Результат.ЕстьВладельцыНастроекПрав Тогда
		Результат.ЕстьОграничениеПоПользователям = Истина;
	Иначе
		Для Каждого СвойстваПоля Из Контекст.СвойстваПолей Цикл
			Если СвойстваПоля.ТипыСохраненияЗначений.Количество() = 0 Тогда
				Продолжить;
			КонецЕсли;
			ОписаниеТипов = Новый ОписаниеТипов(СвойстваПоля.ТипыСохраненияЗначений);
			Если Не Результат.ДляВнешнихПользователей
			   И (    ОписаниеТипов.СодержитТип(Тип("СправочникСсылка.Пользователи"))
			      Или ОписаниеТипов.СодержитТип(Тип("СправочникСсылка.ГруппыПользователей")) ) Тогда
				
				Результат.ЕстьОграничениеПоПользователям = Истина;
				Прервать;
			КонецЕсли;
			Если Результат.ДляВнешнихПользователей
			   И (    ОписаниеТипов.СодержитТип(Тип("СправочникСсылка.ВнешниеПользователи"))
			      Или ОписаниеТипов.СодержитТип(Тип("СправочникСсылка.ГруппыВнешнихПользователей")) ) Тогда
				
				Результат.ЕстьОграничениеПоПользователям = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ДобавитьСвойствоВерсии(Контекст, Результат, "ЕстьОграничениеПоПользователям");
	
КонецПроцедуры

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Результат - см. ПараметрыОграниченияПоСтруктуреОграничения
//  Контекст  - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьНаличиеОграниченияЧтения(Результат, Контекст)
	
	Если Контекст.ЕстьФункцияПравоДоступаИлиРольДоступнаВОграниченииЧтения Тогда
		Результат.ОграничениеЧтенияОтключено = Ложь;
	Иначе
		Для Каждого СвойстваПоля Из Контекст.СвойстваПолей Цикл
			Если СвойстваПоля.Чтение Тогда
				Результат.ОграничениеЧтенияОтключено = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	ДобавитьСвойствоВерсии(Контекст, Результат, "ОграничениеЧтенияОтключено");
	
КонецПроцедуры

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//  Контекст - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьГруппыПолейИДополнительныхТаблиц(Контекст)
	
	// Поля шапки (начинаются от шапки - группировка по пустому псевдониму, если нет имени табличной части).
	// Поля каждой ТЧ (начинаются от табличной части - группировка по имени табличной части).
	// Поля группы связанных дополнительных таблиц (начинаются от экземпляра доп. таблицы - группировка
	// по псевдониму, но если одна таблица ссылается на другую в соединении, тогда образуется группа таблиц).
	
	НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа = 0;
	НомерПоследнейТабличнойЧастиОбъекта = 0;
	НомераТабличныхЧастейОбъекта = Новый Соответствие;
	
	Для Каждого СвойстваПоля Из Контекст.СвойстваПолей Цикл
		Если СвойстваПоля.ПсевдонимТаблицы = "ТекущийСписок"
		   И СвойстваПоля.НесколькоГруппЗначений Тогда
			
			НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа =
				НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа + 1;
			
		ИначеЕсли СвойстваПоля.ПсевдонимТаблицы <> "ТекущийСписок"
		        И СтрНачинаетсяС(СвойстваПоля.ПсевдонимТаблицы, "ТекущийСписок")
			    И НомераТабличныхЧастейОбъекта.Получить(СвойстваПоля.ПсевдонимТаблицы) = Неопределено Тогда
			
			НомерПоследнейТабличнойЧастиОбъекта = НомерПоследнейТабличнойЧастиОбъекта + 1;
			НомераТабличныхЧастейОбъекта.Вставить(СвойстваПоля.ПсевдонимТаблицы, НомерПоследнейТабличнойЧастиОбъекта);
		КонецЕсли;
	КонецЦикла;
	
	РазмерностьКлючаДоступа = УправлениеДоступомСлужебныйПовтИсп.РазмерностьКлючаДоступа();
	
	КоличествоТабличныхЧастейКлючаДляРеквизитовШапкиОбъекта = Цел(
		(НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа
			+ РазмерностьКлючаДоступа.КоличествоРеквизитовТабличнойЧасти - 1)
		/ РазмерностьКлючаДоступа.КоличествоРеквизитовТабличнойЧасти);
	
	ГруппыДополнительныхТаблиц = ГруппыДополнительныхТаблиц(Контекст);
	КоличествоТабличныхЧастейКлюча = НомерПоследнейТабличнойЧастиОбъекта
		+ ГруппыДополнительныхТаблиц.ТаблицыПоГруппам.Количество()
		+ КоличествоТабличныхЧастейКлючаДляРеквизитовШапкиОбъекта;
	
	ЗавершитьПодготовкуПолейТаблицОбъекта(Контекст);
	ЗавершитьПодготовкуОпорныхПолей(Контекст);
	
	Если КоличествоТабличныхЧастейКлюча > РазмерностьКлючаДоступа.КоличествоТабличныхЧастей Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В ограничении доступа списка %1
			           |количество полей, требующих отдельных табличных частей в ключе доступа,
			           |более, чем количество доступных табличных частей в ключе доступа.
			           |
			           |К таким полям относятся:
			           |- поля табличных частей,
			           |- поля дополнительных таблиц, присоединенных к списку,
			           |- поля шапки, у которых значение доступа может иметь более одной группы значений доступа.'"),
			Контекст.Список);
		
		Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
			Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
		КонецЕсли;
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Не Контекст.ЭтоСсылочныйТип
	   И Контекст.ОпорныеПоля.Все.Количество() > Контекст.ОпорныеПоля.МаксимальноеКоличество Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В ограничении доступа списка %1
			           |количество полей списка, используемых в ограничении доступа,
			           |превышает максимально допустимое количество опорных полей: %2.'"),
			Контекст.Список,
			Контекст.ОпорныеПоля.МаксимальноеКоличество);
		
		Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
			Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
		КонецЕсли;
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ГруппыПолей = Новый Соответствие;
	ПсевдонимыТабличныхЧастейОбъекта = Новый Соответствие;
	НомерПоследнегоРеквизитаШапки = 0;
	НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа = 0;
	
	Для Каждого СвойстваПоля Из Контекст.СвойстваПолей Цикл
		Если СвойстваПоля.ПсевдонимТаблицы = "ТекущийСписок" Тогда
			
			Если СвойстваПоля.НесколькоГруппЗначений Тогда
				НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа =
					НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа + 1;
				
				НомерТабличнойЧастиКлюча = Цел(НомерПоследнегоРеквизитаШапкиСНесколькимиГруппамиЗначенийДоступа
					/ РазмерностьКлючаДоступа.КоличествоРеквизитовТабличнойЧасти) + 1;
				
				ИмяГруппыПолей = "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча;
			Иначе
				НомерПоследнегоРеквизитаШапки = НомерПоследнегоРеквизитаШапки + 1;
				Если НомерПоследнегоРеквизитаШапки < 6 Тогда
					ИмяГруппыПолей = "Шапка0";
				ИначеЕсли НомерПоследнегоРеквизитаШапки < 11 Тогда
					ИмяГруппыПолей = "Шапка1";
				ИначеЕсли НомерПоследнегоРеквизитаШапки < 16 Тогда
					ИмяГруппыПолей = "Шапка2";
				Иначе
					ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'В ограничении доступа списка %1
						           |количество полей списка, используемых в ограничении доступа,
						           |превышает максимально допустимое количество: 15.'"),
						Контекст.Список);
					Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
						Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
					КонецЕсли;
					ВызватьИсключение ТекстОшибки;
				КонецЕсли;
			КонецЕсли;
		Иначе
			Если СвойстваПоля.ПсевдонимТаблицы <> "ТекущийСписок"
			   И СтрНачинаетсяС(СвойстваПоля.ПсевдонимТаблицы, "ТекущийСписок") Тогда
				
				НомерТабличнойЧастиКлюча = КоличествоТабличныхЧастейКлючаДляРеквизитовШапкиОбъекта
					+ НомераТабличныхЧастейОбъекта.Получить(СвойстваПоля.ПсевдонимТаблицы);
				
				Если ПсевдонимыТабличныхЧастейОбъекта.Получить(НомерТабличнойЧастиКлюча) = Неопределено Тогда
					ПсевдонимыТабличныхЧастейОбъекта.Вставить(НомерТабличнойЧастиКлюча, СвойстваПоля.ПсевдонимТаблицы)
				КонецЕсли;
			Иначе
				НомерТабличнойЧастиКлюча = КоличествоТабличныхЧастейКлючаДляРеквизитовШапкиОбъекта
					+ НомераТабличныхЧастейОбъекта.Количество()
					+ ГруппыДополнительныхТаблиц.НомераПоПсевдонимам.Получить(СвойстваПоля.ПсевдонимТаблицы);
			КонецЕсли;
			ИмяГруппыПолей = "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча;
		КонецЕсли;
		ГруппаПолей = ГруппыПолей.Получить(ИмяГруппыПолей);
		Если ГруппаПолей = Неопределено Тогда
			ГруппаПолей = Новый Массив;
			ГруппыПолей.Вставить(ИмяГруппыПолей, ГруппаПолей);
		КонецЕсли;
		ГруппаПолей.Добавить(СвойстваПоля);
		СвойстваПоля.Вставить("ИмяГруппыПолейКлючаДоступа", ИмяГруппыПолей);
		СвойстваПоля.Вставить("ИмяРеквизитаГруппыПолейКлючаДоступа", 
			"Значение" + XMLСтрока(ГруппаПолей.Количество() + ?(ИмяГруппыПолей = "Шапка1" Или ИмяГруппыПолей = "Шапка2", 5, 0)));
		
		Если Не СтрНачинаетсяС(ИмяГруппыПолей, "Шапка")
		   И ГруппаПолей.Количество() > РазмерностьКлючаДоступа.КоличествоРеквизитовТабличнойЧасти Тогда
			
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В ограничении доступа списка %1
				           |количество полей одной табличной части, используемых в ограничении доступа,
				           |превышает максимально допустимое количество: %2.'"),
				Контекст.Список,
				РазмерностьКлючаДоступа.КоличествоРеквизитовТабличнойЧасти);
			
			Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
				Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
			КонецЕсли;
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
	КонецЦикла;
	
	Контекст.Вставить("ГруппыПолей",                      ГруппыПолей);
	Контекст.Вставить("ПсевдонимыТабличныхЧастейОбъекта", ПсевдонимыТабличныхЧастейОбъекта);
	Контекст.Вставить("ГруппыДополнительныхТаблиц",       ГруппыДополнительныхТаблиц);
	Контекст.Вставить("КоличествоТабличныхЧастейКлюча",   КоличествоТабличныхЧастейКлюча);
	
	// Расчет числа СоставПолей.
	
	// Шапка и табличные части:    ТЧ4  ТЧ3  ТЧ2  ТЧ1    Ш.
	// Двоичный формат:           0000 0000 0000 0000 0000.
	// Шестнадцатеричный формат:    x0   x0   x0   x0   x0.
	//
	// Например: Ш0=1, ТЧ1=1.
	// Двоичный формат:           0000 0000 0000 0001 0001.
	// Шестнадцатеричный формат:    x0   x0   x0   x1   x1.
	// Число = 1*16^0 + 1*16^1 = 1 + 16 = 17.
	
	СоставПолей = НомерПоследнегоРеквизитаШапки;
	
	Для НомерТабличнойЧастиКлюча = 1 По КоличествоТабличныхЧастейКлюча Цикл
		ИмяТабличнойЧастиКлюча = "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча;
		ГруппаПолей = ГруппыПолей.Получить(ИмяТабличнойЧастиКлюча);
		СоставПолей = СоставПолей + ГруппаПолей.Количество() * Степень16(НомерТабличнойЧастиКлюча);
	КонецЦикла;
	
	Контекст.Вставить("СоставПолей", СоставПолей);
	
КонецПроцедуры

// Для функции ПараметрыОграниченияДоступа.
//
// Параметры:
//   УсловиеРасчета - см. СтруктураРасчетаПрава
//   Условие        - см. ОписаниеУзла
//   Контекст       - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗаполнитьСтруктуруРасчетаПрава(УсловиеРасчета, Условие, Контекст, КореньУсловия = Истина)
	
	Если Не ЗначениеЗаполнено(Условие) Тогда
		Возврат;
	КонецЕсли;
	
	Если КореньУсловия Тогда
		Контекст.Вставить("ТребуемыеРеквизитыТабличныхЧастейКлюча", Новый Соответствие);
		Контекст.Вставить("СтруктураРасчетаПраваСвойстваВерсии", Новый Массив);
	КонецЕсли;
	
	СвойстваПоля = Неопределено;
	ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "Узел", Условие.Узел);
	
	Если Условие.Узел = "Поле" Тогда
		СвойстваПоля = Контекст.СвойстваПолейКлючаДоступа.Получить(Условие);
		
	ИначеЕсли Условие.Узел = "И"
	      Или Условие.Узел = "Или" Тогда
		
		УсловиеРасчета = Новый Структура("Узел, Аргументы", Условие.Узел, Новый Массив);
		Индекс = 0;
		Для Каждого Аргумент Из Условие.Аргументы Цикл
			УсловиеРасчета.Аргументы.Добавить(Неопределено);
			ЗаполнитьСтруктуруРасчетаПрава(УсловиеРасчета.Аргументы[Индекс], Аргумент, Контекст, Ложь);
			Индекс = Индекс + 1;
		КонецЦикла;
		СвойстваПоля = Null;
		
	ИначеЕсли Условие.Узел = "Не" Тогда
		УсловиеРасчета = Новый Структура("Узел, Аргумент", Условие.Узел, Неопределено);
		ЗаполнитьСтруктуруРасчетаПрава(УсловиеРасчета.Аргумент, Условие.Аргумент, Контекст, Ложь);
		СвойстваПоля = Null;
		
	ИначеЕсли Условие.Узел = "ДляВсехСтрок"
	      Или Условие.Узел = "ДляОднойИзСтрок" Тогда
		
		Если Не КореньУсловия Тогда
			ТребуемыеРеквизитыТабличныхЧастейКлюча = Контекст.ТребуемыеРеквизитыТабличныхЧастейКлюча;
			Контекст.Вставить("ТребуемыеРеквизитыТабличныхЧастейКлюча", Новый Соответствие);
		КонецЕсли;
		
		УсловиеРасчета = Новый Структура("Узел, Аргумент", Условие.Узел, Неопределено);
		ЗаполнитьСтруктуруРасчетаПрава(УсловиеРасчета.Аргумент, Условие.Аргумент, Контекст, Ложь);
		
		Если Не КореньУсловия Тогда
			УсловиеРасчета.Вставить("ТребуемыеРеквизитыТабличныхЧастейКлюча", Контекст.ТребуемыеРеквизитыТабличныхЧастейКлюча);
			Контекст.Вставить("ТребуемыеРеквизитыТабличныхЧастейКлюча", ТребуемыеРеквизитыТабличныхЧастейКлюча);
		КонецЕсли;
		СвойстваПоля = Null;
		
	ИначеЕсли Условие.Узел = "ЕстьNull" Тогда
		СвойстваПоля = Контекст.СвойстваПолейКлючаДоступа.Получить(Условие.Аргумент);
		
	ИначеЕсли Условие.Узел = "="
	      Или Условие.Узел = "<>" Тогда
		
		СвойстваПоля = Контекст.СвойстваПолейКлючаДоступа.Получить(Условие.ПервыйАргумент);
		Если СвойстваПоля = Неопределено Тогда
			СвойстваПоля = Контекст.СвойстваПолейКлючаДоступа.Получить(Условие.ВторойАргумент);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "В" Тогда
		СвойстваПоля = Контекст.СвойстваПолейКлючаДоступа.Получить(Условие.Искомое);
		
	ИначеЕсли Условие.Узел = "Выбор" Тогда
		УсловиеРасчета = Новый Структура("Узел, Когда, Иначе", Условие.Узел, Новый Массив, Неопределено);
		Для Каждого Когда Из Условие.Когда Цикл
			СтруктураКогда = Новый Структура("Условие, Значение");
			УсловиеРасчета.Когда.Добавить(СтруктураКогда);
			Если Условие.Выбор = Неопределено Тогда
				ЗаполнитьСтруктуруРасчетаПрава(СтруктураКогда.Условие, Когда.Условие, Контекст, Ложь);
			Иначе
				Свойства = Контекст.СвойстваПолейКлючаДоступа.Получить(Когда.Условие);
				ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "Узел", "Поле");
				УсловиеРасчетаПоле(СтруктураКогда.Условие, Свойства, Контекст);
			КонецЕсли;
			ЗаполнитьСтруктуруРасчетаПрава(СтруктураКогда.Значение, Когда.Значение, Контекст, Ложь);
		КонецЦикла;
		ЗаполнитьСтруктуруРасчетаПрава(УсловиеРасчета.Иначе, Условие.Иначе, Контекст, Ложь);
		СвойстваПоля = Null;
		
	ИначеЕсли Условие.Узел = "ЗначениеРазрешено"
	      Или Условие.Узел = "ЭтоАвторизованныйПользователь"
	      Или Условие.Узел = "ЧтениеОбъектаРазрешено"
	      Или Условие.Узел = "ИзменениеОбъектаРазрешено"
	      Или Условие.Узел = "ЧтениеСпискаРазрешено"
	      Или Условие.Узел = "ИзменениеСпискаРазрешено" Тогда
		
		УсловиеРасчета = Новый Структура("Узел, Поле", Условие.Узел, Неопределено);
		УсловиеРасчета.Вставить("УточненияСравнения", Новый Соответствие);
		Для Каждого КлючИЗначение Из Условие.УточненияСравнения Цикл
			Если КлючИЗначение.Ключ = "Null"
			 Или КлючИЗначение.Ключ = "Неопределено"
			 Или КлючИЗначение.Ключ = "ПустаяСсылка"
			 Или КлючИЗначение.Ключ = "Отключено" Тогда
				УсловиеРасчета.УточненияСравнения.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
			Иначе
				ИмяТипа = СтрЗаменить(КлючИЗначение.Ключ, ".", "Ссылка.");
				УсловиеРасчета.УточненияСравнения.Вставить(Тип(ИмяТипа), КлючИЗначение.Значение);
			КонецЕсли;
		КонецЦикла;
		ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "УточненияСравнения", Условие.УточненияСравнения);
		ЗаполнитьСтруктуруРасчетаПрава(УсловиеРасчета.Поле, Условие.Поле, Контекст, Ложь);
		
		Если УсловиеРасчета.Поле = Null Тогда
			УсловиеРасчета = Новый Структура("Узел, Значение", "Константа", Истина);
		КонецЕсли;
		СвойстваПоля = Null;
		
	ИначеЕсли Условие.Узел = "Константа" Тогда
		УсловиеРасчета = Новый Структура("Узел, Значение", Условие.Узел, Условие.Значение);
		ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "Значение", Условие.Значение);
		СвойстваПоля = Null;
		
	ИначеЕсли Условие.Узел = "ПравоДоступа" Тогда
		УсловиеРасчета = Новый Структура("Узел, ИмяПрава, ПолноеИмяОбъектаМетаданных",
			Условие.Узел, Условие.ИмяПрава, Условие.ПолноеИмяОбъектаМетаданных);
		ДобавитьЗависимостьОтРолей(Контекст, УсловиеРасчета);
		ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "ИмяПрава", Условие.ИмяПрава);
		ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "ПолноеИмяОбъектаМетаданных",
			Условие.ПолноеИмяОбъектаМетаданных);
		СвойстваПоля = Null;
	
	ИначеЕсли Условие.Узел = "РольДоступна" Тогда
		УсловиеРасчета = Новый Структура("Узел, ИмяРоли", Условие.Узел, Условие.ИмяРоли);
		ДобавитьЗависимостьОтРолей(Контекст, УсловиеРасчета);
		ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "ИмяРоли", Условие.ИмяРоли);
		СвойстваПоля = Null;
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При заполнении структуры расчета права %1 на ключи доступа
			           |списка ""%2""
			           |узел не поддерживается ""%3"".'"),
			Контекст.ИмяПрава,
			Контекст.Список,
			Условие.Узел);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если СвойстваПоля = Неопределено Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При заполнении структуры расчета права %1 на ключи доступа
			           |списка ""%2""
			           |свойства поля не определены для узла ""%3"".'"),
			Контекст.ИмяПрава,
			Контекст.Список,
			Условие.Узел);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если СвойстваПоля <> Null Тогда
		УсловиеРасчетаПоле(УсловиеРасчета, СвойстваПоля, Контекст);
	КонецЕсли;
	
	Если КореньУсловия Тогда
		УсловиеРасчета.Вставить("ТребуемыеРеквизитыТабличныхЧастейКлюча",
			Контекст.ТребуемыеРеквизитыТабличныхЧастейКлюча);
		
		ДобавитьЭлементВерсии(Контекст, ?(Контекст.ИмяПрава = "Чтение",
			"СтруктураРасчетаПраваЧтение", "СтруктураРасчетаПраваИзменение"),
			Контекст.СтруктураРасчетаПраваСвойстваВерсии);
		
		Контекст.Удалить("СтруктураРасчетаПраваСвойстваВерсии");
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьСтруктуруРасчетаПрава.
Процедура УсловиеРасчетаПоле(УсловиеРасчета, СвойстваПоля, Контекст)
	
	Если Контекст.СвойстваПолей.Найти(СвойстваПоля) = Неопределено Тогда
		УсловиеРасчета = Null; // Поле удалено из-за неиспользуемых типов значений доступа.
	Иначе
		УсловиеРасчета = Новый Структура("Узел", "Поле");
		УсловиеРасчета.Вставить("Таблица",  СвойстваПоля.ИмяГруппыПолейКлючаДоступа);
		УсловиеРасчета.Вставить("Реквизит", СвойстваПоля.ИмяРеквизитаГруппыПолейКлючаДоступа);
		Если СтрНачинаетсяС(УсловиеРасчета.Таблица, "ТабличнаяЧасть") Тогда
			Реквизиты = Контекст.ТребуемыеРеквизитыТабличныхЧастейКлюча.Получить(УсловиеРасчета.Таблица);
			Если Реквизиты = Неопределено Тогда
				Реквизиты = Новый Массив;
				Контекст.ТребуемыеРеквизитыТабличныхЧастейКлюча.Вставить(УсловиеРасчета.Таблица, Реквизиты);
			КонецЕсли;
			Если Реквизиты.Найти(УсловиеРасчета.Реквизит) = Неопределено Тогда
				Реквизиты.Добавить(УсловиеРасчета.Реквизит);
			КонецЕсли;
		КонецЕсли;
		ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "Таблица",  УсловиеРасчета.Таблица);
		ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "Реквизит", УсловиеРасчета.Реквизит);
		Если СвойстваПоля.Свойство("ПроверкаЕстьNull") Тогда
			УсловиеРасчета.Вставить("ПроверкаЕстьNull");
			ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, "ПроверкаЕстьNull", Истина);
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьСтруктуруРасчетаПрава.
Процедура ДобавитьЗависимостьОтРолей(Контекст, УсловиеРасчета)
	
	Если УсловиеРасчета.Свойство("ИмяРоли") Тогда
		Роль = Метаданные.Роли.Найти(УсловиеРасчета.ИмяРоли);
		Контекст.ВедущиеРоли.Вставить(Роль.Имя, Истина);
		Возврат;
	КонецЕсли;
	
	ИмяСтандартногоРеквизита = Неопределено;
	ОбъектМетаданных = ОбъектМетаданныхПоПолномуИмениДляПроверкиПрава(
		УсловиеРасчета.ПолноеИмяОбъектаМетаданных, ИмяСтандартногоРеквизита);
	
	Для Каждого Роль Из Метаданные.Роли Цикл
		Если ПравоДоступа(УсловиеРасчета.ИмяПрава, ОбъектМетаданных, Роль, ИмяСтандартногоРеквизита) Тогда
			Контекст.ВедущиеРоли.Вставить(Роль.Имя, Истина);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ЗаполнитьСтруктуруРасчетаПрава, УсловиеРасчетаПоле.
Процедура ДобавитьСвойствоВерсииСтруктурыРасчета(Контекст, Имя, Значение);
	
	Если ТипЗнч(Значение) = Тип("Соответствие") Тогда
		СписокЗначений = Новый СписокЗначений;
		Для Каждого КлючИЗначение Из Значение Цикл
			СписокЗначений.Добавить(КлючИЗначение.Значение, КлючИЗначение.Ключ);
		КонецЦикла;
		СписокЗначений.СортироватьПоПредставлению();
		Для Каждого ЭлементСписка Из СписокЗначений Цикл
			Контекст.СтруктураРасчетаПраваСвойстваВерсии.Добавить(ЭлементСписка.Представление);
			Контекст.СтруктураРасчетаПраваСвойстваВерсии.Добавить(ЭлементСписка.Значение);
		КонецЦикла;
	Иначе
		Контекст.СтруктураРасчетаПраваСвойстваВерсии.Добавить(Имя);
		Контекст.СтруктураРасчетаПраваСвойстваВерсии.Добавить(Значение);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьГруппыПолейИДополнительныхТаблиц.
Функция Степень16(Степень)
	
	Степень16 = 1;
	
	Для Счетчик = 1 По Степень Цикл
		Степень16 = Степень16 * 16;
	КонецЦикла;
	
	Возврат Степень16;
	
КонецФункции

// Для функции ЗаполнитьГруппыПолейИДополнительныхТаблиц.
// 
// Параметры:
//  Контекст - см. КонтекстПараметровПоСтруктуреОграничения
//
// Возвращаемое значение:
//  Структура:
//    * НомераПоПсевдонимам - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - псевдоним дополнительной таблицы
//        ** Значение - Число  - номер группы дополнительных таблиц
//         
//    * ТаблицыПоГруппам - Соответствие из КлючИЗначение:
//        ** Ключ     - Число - номер группы дополнительных таблиц
//        ** Значение - Массив из см. НовоеОписаниеСоединения
//        
//    * ПсевдонимыТаблицСПолями - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - псевдоним дополнительной таблицы
//                               с полями ключа доступа (кроме полей соединений).
//        ** Значение - Булево - Истина.
//
Функция ГруппыДополнительныхТаблиц(Контекст)
	
	ДополнительныеТаблицы = Контекст.СтруктураОграничения.ДополнительныеТаблицы;
	НомераГруппПоПсевдонимам = Новый Соответствие;
	
	Контекст.ОпорныеПоля.Вставить("ПоДополнительнымТаблицам", Новый Соответствие);
	Контекст.ПоляТаблицОбъекта.Вставить("ПоДополнительнымТаблицам", Новый Соответствие);
	Контекст.Вставить("ПоляУсловияСоединенияДополнительныхТаблиц", Новый Массив);
	
	ПоследняяГруппа = 0;
	Для Каждого ДополнительнаяТаблица Из ДополнительныеТаблицы Цикл
		ДополнительнаяТаблица.Вставить("ПсевдонимыТребуемыхТаблиц", Новый Массив);
		ДополнительнаяТаблица.Вставить("ПоляУсловияСоединения", Новый Массив);
		ДополнительнаяТаблица.Вставить("ПолеПроверкиСоединения", "");
		
		ТекстУсловияСоединения = ТекстУсловияСоединения(ДополнительнаяТаблица, Контекст);
		ДополнительнаяТаблица.Вставить("ТекстУсловияСоединения", ?(Лев(ТекстУсловияСоединения, 1) = "(",
			ТекстУсловияСоединения, "(" + ТекстУсловияСоединения + ")"));
		
		Если Не ЗначениеЗаполнено(ДополнительнаяТаблица.ПолеПроверкиСоединения) Тогда
			ПоляКлючаДоступа = Контекст.ПоляКлючаДоступаПослеУпрощения;
			Для Каждого ОписаниеПоля Из ПоляКлючаДоступа Цикл
				Если ОписаниеПоля.Поле.Псевдоним = ДополнительнаяТаблица.Псевдоним Тогда
					ДополнительнаяТаблица.ПолеПроверкиСоединения =
						ДополнительнаяТаблица.Псевдоним + "." + ОписаниеПоля.Поле.Имя;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		ТекущаяГруппа = НомераГруппПоПсевдонимам.Получить(ДополнительнаяТаблица.Псевдоним);
		Если ТекущаяГруппа = Неопределено Тогда
			ПоследняяГруппа = ПоследняяГруппа + 1;
			ТекущаяГруппа = ПоследняяГруппа;
			НомераГруппПоПсевдонимам.Вставить(ДополнительнаяТаблица.Псевдоним, ТекущаяГруппа);
		КонецЕсли;
		
		Для Каждого Псевдоним Из ДополнительнаяТаблица.ПсевдонимыТребуемыхТаблиц Цикл
			ГруппаТребуемойТаблицы = НомераГруппПоПсевдонимам.Получить(Псевдоним);
			Если ГруппаТребуемойТаблицы = Неопределено Тогда
				НомераГруппПоПсевдонимам.Вставить(Псевдоним, ТекущаяГруппа);
				Продолжить;
			КонецЕсли;
			Если ГруппаТребуемойТаблицы = ТекущаяГруппа Тогда
				Продолжить;
			КонецЕсли;
			ПсевдонимыЗаменяемойГруппы = Новый Массив;
			Для Каждого КлючИЗначение Из НомераГруппПоПсевдонимам Цикл
				Если КлючИЗначение.Значение <> ГруппаТребуемойТаблицы Тогда
					Продолжить;
				КонецЕсли;
				ПсевдонимыЗаменяемойГруппы.Добавить(КлючИЗначение.Ключ);
			КонецЦикла;
			Для Каждого Псевдоним Из ПсевдонимыЗаменяемойГруппы Цикл
				НомераГруппПоПсевдонимам.Вставить(Псевдоним, ТекущаяГруппа);
			КонецЦикла;
		КонецЦикла;
	КонецЦикла;
	
	ПсевдонимыТаблицСПолями = Новый Соответствие;
	Для Каждого СвойстваПоля Из Контекст.СвойстваПолей Цикл
		Если СвойстваПоля.ПсевдонимТаблицы = "ТекущийСписок" Тогда
			Продолжить;
		КонецЕсли;
		ПсевдонимыТаблицСПолями.Вставить(СвойстваПоля.ПсевдонимТаблицы, Истина);
	КонецЦикла;
	
	ИспользованиеГрупп = Новый Соответствие;
	Для Группа = 1 По ПоследняяГруппа Цикл
		Для Каждого КлючИЗначение Из НомераГруппПоПсевдонимам Цикл
			Если КлючИЗначение.Значение <> Группа Тогда
				Продолжить;
			КонецЕсли;
			Если ИспользованиеГрупп.Получить(Группа) = Неопределено Тогда
				ИспользованиеГрупп.Вставить(Группа, Ложь);
			КонецЕсли;
			Если ПсевдонимыТаблицСПолями.Получить(КлючИЗначение.Ключ) <> Неопределено Тогда
				ИспользованиеГрупп.Вставить(Группа, Истина);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	НоваяГруппа = 1;
	НовыеГруппы = Новый Соответствие;
	Для Каждого ДополнительнаяТаблица Из ДополнительныеТаблицы Цикл
		СтараяГруппа = НомераГруппПоПсевдонимам.Получить(ДополнительнаяТаблица.Псевдоним);
		Если Не ИспользованиеГрупп.Получить(СтараяГруппа) Тогда
			Продолжить;
		КонецЕсли;
		Если НовыеГруппы.Получить(СтараяГруппа) <> Неопределено Тогда
			Продолжить;
		КонецЕсли;
		НовыеГруппы.Вставить(СтараяГруппа, НоваяГруппа);
		НоваяГруппа = НоваяГруппа + 1;
	КонецЦикла;
	
	Группы = Новый Структура;
	Группы.Вставить("НомераПоПсевдонимам",     Новый Соответствие);
	Группы.Вставить("ТаблицыПоГруппам",        Новый Соответствие);
	Группы.Вставить("ПсевдонимыТаблицСПолями", ПсевдонимыТаблицСПолями);
	
	Для Каждого ДополнительнаяТаблица Из ДополнительныеТаблицы Цикл
		СтараяГруппа = НомераГруппПоПсевдонимам.Получить(ДополнительнаяТаблица.Псевдоним);
		Если Не ИспользованиеГрупп.Получить(СтараяГруппа) Тогда
			Продолжить;
		КонецЕсли;
		УстановитьИспользованиеПоляТаблицыОбъекта(Контекст, , ДополнительнаяТаблица);
		УстановитьИспользованиеОпорногоПоля(Контекст, , ДополнительнаяТаблица);
		Группа = НовыеГруппы.Получить(СтараяГруппа);
		Группы.НомераПоПсевдонимам.Вставить(ДополнительнаяТаблица.Псевдоним, Группа);
		ГруппаТаблиц = Группы.ТаблицыПоГруппам.Получить(Группа);
		Если ГруппаТаблиц = Неопределено Тогда
			ГруппаТаблиц = Новый Массив;
			Группы.ТаблицыПоГруппам.Вставить(Группа, ГруппаТаблиц);
		КонецЕсли;
		ГруппаТаблиц.Добавить(ДополнительнаяТаблица);
	КонецЦикла;
	
	Для Каждого СвойстваПоля Из Контекст.СвойстваПолей Цикл
		ТекущиеСвойства = СвойстваПоля;
		Пока ТекущиеСвойства <> Неопределено Цикл
			ДобавитьВедущиеСпискиПоЗначениямПолей(Контекст,
				ТекущиеСвойства.УзелПоле,
				ТекущиеСвойства.СвойстваВложения,
				?(ТекущиеСвойства.ЭтоПолеСписка, Неопределено, ТекущиеСвойства.ПсевдонимТаблицы));
			ТекущиеСвойства = ТекущиеСвойства.СвойстваВложения;
		КонецЦикла;
	КонецЦикла;
	
	Для Каждого ОписаниеПоляУсловия Из Контекст.ПоляУсловияСоединенияДополнительныхТаблиц Цикл
		Группа = Группы.НомераПоПсевдонимам.Получить(ОписаниеПоляУсловия.ПсевдонимТаблицыУсловия);
		Если Группа <> Неопределено Тогда
			УзелПоле = ОписаниеПоляУсловия.УзелПоле;
			ДобавитьСвойстваВерсии(Контекст, УзелПоле, "Псевдоним, Имя, ТипыСтрокой");
			ДобавитьВедущиеСпискиПоЗначениямПолей(Контекст, УзелПоле,,
				Группы.ТаблицыПоГруппам.Получить(Группа), ОписаниеПоляУсловия.ПсевдонимТаблицыУсловия);
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Описание Из Контекст.ВедущиеСпискиПоЗначениямПолей.СоединенияОтборов Цикл
		ЗаполнитьОтборыПоЗначениямПолейВедущегоСписка(Описание.Ключ,
			Описание.Значение, Группы, Контекст);
	КонецЦикла;
	ЗаполнитьОтборыВедущихСписковПоПолюСсылка(Контекст.ВедущиеСпискиПоКлючамДоступа, Группы, Контекст);
	ЗаполнитьОтборыВедущихСписковПоПолюСсылка(Контекст.ВедущиеСпискиПоЗначениямСГруппами, Группы, Контекст);
	
	Возврат Группы;
	
КонецФункции

// Для функции ГруппыДополнительныхТаблиц.
Функция ТекстУсловияСоединения(ДополнительнаяТаблица, Контекст,
			Условие = Null, Поле = Неопределено, Псевдоним = Неопределено)
	
	Если Условие = Null Тогда
		Условие = ДополнительнаяТаблица.УсловиеСоединения;
	КонецЕсли;
	
	// Возможные узлы: "Поле", "Значение", "Константа", "И", "=".
	
	Если Условие.Узел = "Поле" Тогда
		Контекст.ПоляУсловияСоединенияДополнительныхТаблиц.Добавить(Новый Структура(
			"УзелПоле, ПсевдонимТаблицыУсловия", Условие, ДополнительнаяТаблица.Псевдоним));
		ДобавитьПолеТаблицыОбъекта(Контекст, Условие, , ДополнительнаяТаблица);
		Если Условие.Псевдоним = Контекст.СтруктураОграничения.ПсевдонимОсновнойТаблицы Тогда
			ДобавитьОпорноеПоле(Контекст, Условие, , ДополнительнаяТаблица);
			Псевдоним = "ТекущийСписок";
			Поле = Псевдоним + "." + Условие.Имя;
			Возврат ИмяПоляСРазверткойОпорногоПоляПоТипам(Псевдоним, Условие);
		КонецЕсли;
		Псевдоним = Условие.Псевдоним;
		Если Псевдоним = ДополнительнаяТаблица.Псевдоним Тогда
			Если ДополнительнаяТаблица.ПолеПроверкиСоединения = "" Тогда
				ДополнительнаяТаблица.ПолеПроверкиСоединения = Псевдоним + "." + Условие.Имя;
			КонецЕсли;
		ИначеЕсли ДополнительнаяТаблица.ПсевдонимыТребуемыхТаблиц.Найти(Псевдоним) = Неопределено Тогда
			ДополнительнаяТаблица.ПсевдонимыТребуемыхТаблиц.Добавить(Псевдоним);
		КонецЕсли;
		Поле = Псевдоним + "." + Условие.Имя;
		Возврат Поле;
	КонецЕсли;
	
	Если Условие.Узел = "Значение"
	 Или Условие.Узел = "Константа" Тогда
		
		Поле = ВыражениеУзлаЗначениеИлиКонстанта(Условие);
		Возврат Поле;
	КонецЕсли;
	
	Если Условие.Узел = "И" Тогда
		Текст = "";
		Для Каждого Аргумент Из Условие.Аргументы Цикл
			Текст = Текст + ?(Текст = "", "", Символы.ПС + "И ");
			Текст = Текст + ТекстУсловияСоединения(ДополнительнаяТаблица, Контекст, Аргумент);
		КонецЦикла;
		Возврат Текст;
	КонецЕсли;
	
	Если Условие.Узел = "=" Тогда
		ПервоеПоле = НовоеПолеУсловияСоединения();
		ВтороеПоле = НовоеПолеУсловияСоединения();
		
		ПервыйАргумент = ТекстУсловияСоединения(ДополнительнаяТаблица,
			Контекст, Условие.ПервыйАргумент, ПервоеПоле.Поле, ПервоеПоле.Псевдоним);
		
		ВторойАргумент = ТекстУсловияСоединения(ДополнительнаяТаблица,
			Контекст, Условие.ВторойАргумент, ВтороеПоле.Поле, ВтороеПоле.Псевдоним);
		
		Если ПервоеПоле.Псевдоним = ДополнительнаяТаблица.Псевдоним
		 Или Не ЗначениеЗаполнено(ВтороеПоле.Псевдоним) Тогда
			ТекстУсловия = "(" + ПервыйАргумент + " = " + ВторойАргумент + ")";
			ПоляУсловия = ПараПолейУсловияСоединения(ПервоеПоле, ВтороеПоле);
		Иначе
			ТекстУсловия = "(" + ВторойАргумент + " = " + ПервыйАргумент + ")";
			ПоляУсловия = ПараПолейУсловияСоединения(ВтороеПоле, ПервоеПоле);
		КонецЕсли;
		ДополнительнаяТаблица.ПоляУсловияСоединения.Добавить(ПоляУсловия);
		Возврат ТекстУсловия;
	КонецЕсли;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Не определена обработка узла ""%1""'"), Условие.Узел);
	
	ВызватьИсключение ТекстОшибки;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * ПервоеПоле - см. НовоеПолеУсловияСоединения
//    * ВтороеПоле - см. НовоеПолеУсловияСоединения
//
Функция ПараПолейУсловияСоединения(ПервоеПоле, ВтороеПоле)
	
	Возврат Новый Структура("ПервоеПоле, ВтороеПоле", ПервоеПоле, ВтороеПоле);
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * Поле      - Строка
//    * Псевдоним - Строка
//
Функция НовоеПолеУсловияСоединения()
	
	Возврат Новый Структура("Поле, Псевдоним");
	
КонецФункции

// Для функций СвойстваПоля, ТекстУсловияСоединения.
Процедура ДобавитьПолеТаблицыОбъекта(Контекст, УзелПоле, СвойстваПоля = Неопределено, ДополнительнаяТаблица = Неопределено, ТабличнаяЧасть = "")
	
	Если Не Контекст.ЭтоСсылочныйТип Тогда
		Возврат;
	КонецЕсли;
	
	ИмяТаблицыОбъекта = Контекст.Список;
	
	Если ЗначениеЗаполнено(УзелПоле.Псевдоним)
	   И УзелПоле.Псевдоним <> Контекст.СтруктураОграничения.ПсевдонимОсновнойТаблицы
	   И ВРег(Контекст.Список) <> ВРег(УзелПоле.Таблица) Тогда
		
		ЧастиИмениТаблицы = СтрРазделить(УзелПоле.Таблица, ".", Ложь);
		Если ЧастиИмениТаблицы.Количество() <> 3 Тогда
			Возврат;
		КонецЕсли;
		ИмяТаблицыОбъекта = Контекст.Список + "." + ЧастиИмениТаблицы[2];
	КонецЕсли;
	
	ЧастиИмениПоля = СтрРазделить(УзелПоле.Имя, ".", Ложь);
	Если ЗначениеЗаполнено(ТабличнаяЧасть) Тогда
		ИмяПоляТаблицы = ЧастиИмениПоля[1];
		ТипыПоляТаблицы = УзелПоле.ТипыПоля[1];
		ИмяТаблицыОбъекта = Контекст.Список + "." + ТабличнаяЧасть;
	Иначе
		ИмяПоляТаблицы = ЧастиИмениПоля[0];
		ТипыПоляТаблицы = УзелПоле.ТипыПоля[0];
	КонецЕсли;
	
	ПоляТаблицОбъекта = Контекст.ПоляТаблицОбъекта;
	ИмяТаблицыОбъекта = СтрЗаменить(ИмяТаблицыОбъекта, ".", "_");
	
	Если СвойстваПоля <> Неопределено Тогда
		ОписаниеПоляТаблицы = Новый Структура("Таблица, Поле", ИмяТаблицыОбъекта, ИмяПоляТаблицы);
		ПоляТаблицОбъекта.ПоСвойствамПолей.Вставить(СвойстваПоля, ОписаниеПоляТаблицы);
	КонецЕсли;
	
	Если ДополнительнаяТаблица <> Неопределено Тогда
		ПоляТаблиц = ПоляТаблицОбъекта.ПоДополнительнымТаблицам.Получить(ДополнительнаяТаблица);
		Если ПоляТаблиц = Неопределено Тогда
			ПоляТаблиц = Новый Структура;
			ПоляТаблицОбъекта.ПоДополнительнымТаблицам.Вставить(ДополнительнаяТаблица, ПоляТаблиц);
		КонецЕсли;
		Если Не ПоляТаблиц.Свойство(ИмяТаблицыОбъекта) Тогда
			ПоляТаблиц.Вставить(ИмяТаблицыОбъекта, Новый Массив);
		КонецЕсли;
		ПоляТаблицы = ПоляТаблиц[ИмяТаблицыОбъекта]; // Массив
		ПоляТаблицы.Добавить(ИмяПоляТаблицы);
	КонецЕсли;
	
	Если Не ПоляТаблицОбъекта.Состав.Свойство(ИмяТаблицыОбъекта) Тогда
		ПоляТаблицОбъекта.Состав.Вставить(ИмяТаблицыОбъекта, Новый Структура);
	КонецЕсли;
	ПоляТаблицы = ПоляТаблицОбъекта.Состав[ИмяТаблицыОбъекта];
	Если Не ПоляТаблицы.Свойство(ИмяПоляТаблицы) Тогда
		ПоляТаблицы.Вставить(ИмяПоляТаблицы, Новый Структура("Тип, Использование", ТипыПоляТаблицы, Ложь));
	КонецЕсли;
	
КонецПроцедуры

// Для функций СвойстваПоля, ТекстУсловияСоединения.
Процедура ДобавитьОпорноеПоле(Контекст, УзелПоле, СвойстваПоля = Неопределено, ДополнительнаяТаблица = Неопределено)
	
	Если Не УзелПоле.Свойство("ОсновнойПорядок") Тогда
		Возврат;
	КонецЕсли;
	
	Позиция = СтрНайти(УзелПоле.Имя, ".");
	Если Позиция = 0 Тогда
		ИмяОпорногоПоля = УзелПоле.Имя;
	Иначе
		ИмяОпорногоПоля = Лев(УзелПоле.Имя, Позиция - 1);
	КонецЕсли;
	
	ОпорныеПоля = Контекст.ОпорныеПоля;
	
	Если СвойстваПоля <> Неопределено Тогда
		ОпорныеПоля.ПоСвойствамПолей.Вставить(СвойстваПоля, ИмяОпорногоПоля);
	КонецЕсли;
	
	Если ДополнительнаяТаблица <> Неопределено Тогда
		ИменаОпорныхПолей = ОпорныеПоля.ПоДополнительнымТаблицам.Получить(ДополнительнаяТаблица);
		Если ИменаОпорныхПолей = Неопределено Тогда
			ИменаОпорныхПолей = Новый Массив;
			ОпорныеПоля.ПоДополнительнымТаблицам.Вставить(ДополнительнаяТаблица, ИменаОпорныхПолей);
		КонецЕсли;
		ИменаОпорныхПолей.Добавить(ИмяОпорногоПоля);
	КонецЕсли;
	
	Если ОпорныеПоля.Список.НайтиПоЗначению(ИмяОпорногоПоля) = Неопределено Тогда
		ОпорныеПоля.Список.Добавить(ИмяОпорногоПоля, УзелПоле.ОсновнойПорядок);
		ТипыОпорногоПоля = УзелПоле.ТипыПоля[0];
		ОпорныеПоля.ТипыПоИменамПолей.Вставить(ИмяОпорногоПоля, ТипыОпорногоПоля);
		Если Не ЗначениеЗаполнено(Контекст.ИмяОтдельногоРегистраКлючей) Тогда
			ЗапрещенныеТипы = Новый Массив;
			Если ЕстьПростойТип(ТипыОпорногоПоля) Тогда
				Для Каждого Тип Из ТипыОпорногоПоля.Типы() Цикл
					Если ЭтоПростойТип(Тип) Тогда
						ЗапрещенныеТипы.Добавить(Строка(Тип));
					КонецЕсли;
				КонецЦикла;
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'В ограничении доступа списка %1
					           |опорное поле %2 содержит простые типы: %3.
					           |
					           |Это недопустимо при использовании общего регистра сведений %4.
					           |Либо исключите простые типы из состава типов опорного поля,
					           |либо создайте отдельный регистр ключей доступа для этого списка.'"),
					Контекст.Список,
					ИмяОпорногоПоля,
					СтрСоединить(ЗапрещенныеТипы, ", "),
					"КлючиДоступаКРегистрам");
				
				Если Контекст.Свойство("ОшибкаПриВызовеИсключения") Тогда
					Контекст.ОшибкаПриВызовеИсключения.Текст = ТекстОшибки;
				КонецЕсли;
				ВызватьИсключение ТекстОшибки;
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедур УдалитьПоляНеиспользуемыхВидовДоступа, ГруппыДополнительныхТаблиц.
Процедура УстановитьИспользованиеПоляТаблицыОбъекта(Контекст, СвойстваПоля = Неопределено, ДополнительнаяТаблица = Неопределено)
	
	Если Не Контекст.ЭтоСсылочныйТип Тогда
		Возврат;
	КонецЕсли;
	
	ПоляТаблицОбъекта = Контекст.ПоляТаблицОбъекта;
	
	Если СвойстваПоля <> Неопределено Тогда
		ОписаниеПоля = ПоляТаблицОбъекта.ПоСвойствамПолей.Получить(СвойстваПоля);
		Если ОписаниеПоля <> Неопределено Тогда
			ПоляТаблицОбъекта.Состав[ОписаниеПоля.Таблица][ОписаниеПоля.Поле].Использование = Истина;
		КонецЕсли;
	Иначе
		ПоляТаблиц = ПоляТаблицОбъекта.ПоДополнительнымТаблицам.Получить(ДополнительнаяТаблица);
		Если ПоляТаблиц <> Неопределено Тогда
			Для Каждого ОписаниеПолей Из ПоляТаблиц Цикл
				ПоляТаблицы = ПоляТаблицОбъекта.Состав[ОписаниеПолей.Ключ];
				Для Каждого ИмяПоля Из ОписаниеПолей.Значение Цикл
					ПоляТаблицы[ИмяПоля].Использование = Истина;
				КонецЦикла;
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедур УдалитьПоляНеиспользуемыхВидовДоступа, ГруппыДополнительныхТаблиц.
Процедура УстановитьИспользованиеОпорногоПоля(Контекст, СвойстваПоля = Неопределено, ДополнительнаяТаблица = Неопределено)
	
	ОпорныеПоля = Контекст.ОпорныеПоля;
	
	Если СвойстваПоля <> Неопределено Тогда
		ИмяОпорногоПоля = ОпорныеПоля.ПоСвойствамПолей.Получить(СвойстваПоля);
		Если ИмяОпорногоПоля <> Неопределено Тогда
			ОпорныеПоля.Список.НайтиПоЗначению(ИмяОпорногоПоля).Пометка = Истина;
		КонецЕсли;
	Иначе
		ИменаОпорныхПолей = ОпорныеПоля.ПоДополнительнымТаблицам.Получить(ДополнительнаяТаблица);
		Если ИменаОпорныхПолей <> Неопределено Тогда
			Для Каждого ИмяОпорногоПоля Из ИменаОпорныхПолей Цикл
				ОпорныеПоля.Список.НайтиПоЗначению(ИмяОпорногоПоля).Пометка = Истина;
			КонецЦикла;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьГруппыПолейИДополнительныхТаблиц.
//
// Параметры:
//  Контекст - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗавершитьПодготовкуПолейТаблицОбъекта(Контекст)
	
	ПоляТаблицОбъекта = Контекст.ПоляТаблицОбъекта;
	
	Если Не Контекст.ЭтоСсылочныйТип Тогда
		ПоляТаблицОбъекта.Удалить("ПоСвойствамПолей");
		ПоляТаблицОбъекта.Удалить("ПоДополнительнымТаблицам");
		Возврат;
	КонецЕсли;
	
	ПолноеИмяОсновнойТаблицы = СтрЗаменить(Контекст.Список, ".", "_");
	ТипСсылки = Новый ОписаниеТипов(ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(
		Тип(ИмяТипаСсылки(Контекст.Список, Контекст.СтруктураОграничения.ВнутренниеДанные.ТипыТаблицПоИменам))));
	
	Для Каждого ОписаниеТаблицы Из ПоляТаблицОбъекта.Состав Цикл
		ПоляТаблицы = НовыеПоляТаблицыОбъекта();
		ПоляТаблицы.ПолноеИмяТаблицы = ОписаниеТаблицы.Ключ;
		ТаблицаЗначений = Новый ТаблицаЗначений;
		ЕстьИспользуемыеПоля = Ложь;
		Для Каждого ОписаниеПоля Из ОписаниеТаблицы.Значение Цикл
			Если Не ОписаниеПоля.Значение.Использование Тогда
				Продолжить;
			КонецЕсли;
			ЕстьИспользуемыеПоля = Истина;
			Если ВРег("Ссылка") = ВРег(ОписаниеПоля.Ключ) Тогда
				Продолжить;
			КонецЕсли;
			ПоляТаблицы.Поля.Добавить(ОписаниеПоля.Ключ);
			ТаблицаЗначений.Колонки.Добавить(ОписаниеПоля.Ключ, ОписаниеПоля.Значение.Тип);
		КонецЦикла;
		Если Не ЕстьИспользуемыеПоля Тогда
			Продолжить;
		КонецЕсли;
		ПоляТаблицы.СписокПолей = СтрСоединить(ПоляТаблицы.Поля, ", ");
		ПоляТаблицы.Поля.Вставить(0, "Ссылка");
		ТаблицаЗначений.Колонки.Добавить("Ссылка", ТипСсылки);
		ПоляТаблицы.ТаблицаСПолями = Новый ХранилищеЗначения(ТаблицаЗначений);
		Если ПоляТаблицы.ПолноеИмяТаблицы <> ПолноеИмяОсновнойТаблицы Тогда
			ПоляТаблицы.ТабличнаяЧасть = Сред(ПоляТаблицы.ПолноеИмяТаблицы,
				СтрДлина(ПолноеИмяОсновнойТаблицы) + 2);
		КонецЕсли;
		ПоляТаблицОбъекта.Результат.Добавить(ПоляТаблицы);
	КонецЦикла;
	
	ПоляТаблицОбъекта.Удалить("ПоСвойствамПолей");
	ПоляТаблицОбъекта.Удалить("ПоДополнительнымТаблицам");
	
КонецПроцедуры

// Для процедуры ЗаполнитьГруппыПолейИДополнительныхТаблиц.
//
// Параметры:
//  Контекст - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ЗавершитьПодготовкуОпорныхПолей(Контекст)
	
	ОпорныеПоля = Контекст.ОпорныеПоля;
	ОпорныеПоля.Список.СортироватьПоПредставлению();
	
	Для Каждого ЭлементСписка Из ОпорныеПоля.Список Цикл
		ОпорныеПоля.Все.Добавить(ЭлементСписка.Значение);
		ОпорныеПоля.ТипыВсех.Добавить(Новый ХранилищеЗначения(
			ОпорныеПоля.ТипыПоИменамПолей.Получить(ЭлементСписка.Значение)));
		
		Если ЭлементСписка.Пометка Тогда
			ОпорныеПоля.Используемые.Добавить(ЭлементСписка.Значение);
			ОпорныеПоля.ТипыИспользуемых.Добавить(Новый ХранилищеЗначения(
				ОпорныеПоля.ТипыПоИменамПолей.Получить(ЭлементСписка.Значение)));
		КонецЕсли;
	КонецЦикла;
	
	ОпорныеПоля.Удалить("Список");
	ОпорныеПоля.Удалить("ТипыПоИменамПолей");
	ОпорныеПоля.Удалить("ПоСвойствамПолей");
	ОпорныеПоля.Удалить("ПоДополнительнымТаблицам");
	
КонецПроцедуры

// Для функций ТекстУсловияСоединения, СвойстваПоля.
Функция ИмяПоляСРазверткойОпорногоПоляПоТипам(Псевдоним, УзелПоле)
	
	Если Не УзелПоле.Свойство("ОсновнойПорядок")
	 Или Не УзелПоле.Свойство("ТаблицыСледующегоПоля") Тогда
		
		Возврат Псевдоним + "." + УзелПоле.Имя;
	КонецЕсли;
	
	КоличествоТаблиц = УзелПоле.ТаблицыСледующегоПоля[0].Количество();
	
	Если КоличествоТаблиц >= 70 Тогда // Ограничение платформы.
		Возврат Псевдоним + "." + УзелПоле.Имя;
	КонецЕсли;
	
	Позиция = СтрНайти(УзелПоле.Имя, ".");
	ОпорноеПоле = Псевдоним + "." + Лев(УзелПоле.Имя, Позиция - 1);
	ОстальныеПоля = Сред(УзелПоле.Имя, Позиция + 1);
	
	ИмяПоля = "";
	Для Каждого ИмяТипа Из УзелПоле.ТаблицыСледующегоПоля[0] Цикл
		ТекущееИмяПоля = СтрШаблон("ВЫРАЗИТЬ (%1 КАК %2).%3", ОпорноеПоле,  ИмяТипа, ОстальныеПоля); // @query-part-1
		Если ЗначениеЗаполнено(ИмяПоля) Тогда
			ИмяПоля = // @query-part-1
				"ЕСТЬNULL(" + ТекущееИмяПоля + ",
				|	" + ТекстСОтступом(ИмяПоля, "	") + ")";
		Иначе
			ИмяПоля = ТекущееИмяПоля;
		КонецЕсли;
	КонецЦикла;
	
	Возврат ИмяПоля;
	
КонецФункции

// Для функции ПараметрыОграниченияДоступа.
Процедура ЗаполнитьСвойстваПолей(Контекст)
	
	// 1. Для полей-аргументов узлов сравнения  =, <>, В, ЕстьNull
	// вычисляется результат сравнения и сохраняется в ключе.
	
	// 2. Для значений типа Булево сохраняются значения:
	// - Перечисления.ДополнительныеЗначенияДоступа.ЗначениеИстина,
	// - Перечисления.ДополнительныеЗначенияДоступа.ЗначениеЛожь.
	
	// 3. Для значений типов Число, Дата, Строка сохраняется результат
	//    сравнения со значением Истина, как указано в пункте 2.
	
	Контекст.ОпорныеПоля.Вставить("ТипыПоИменамПолей", Новый Соответствие);
	Контекст.ОпорныеПоля.Вставить("ПоСвойствамПолей",  Новый Соответствие);
	Контекст.ПоляТаблицОбъекта.Вставить("ПоСвойствамПолей", Новый Соответствие);
	
	СвойстваВсехПолей = Новый Соответствие;
	ПоляКлючаДоступаДоУпрощения = Контекст.СтруктураОграничения.ВнутренниеДанные.ПоляКлючаДоступа;
	Для Каждого ОписаниеПоля Из ПоляКлючаДоступаДоУпрощения Цикл
		СвойстваПоля = СвойстваПоля(ОписаниеПоля.Поле, Контекст);
		СвойстваВсехПолей.Вставить(ОписаниеПоля.Поле, СвойстваПоля);
	КонецЦикла;
	Контекст.Вставить("СвойстваВсехПолей", СвойстваВсехПолей);
	
	Контекст.Вставить("ОставшиесяПоляПослеУпрощения", Новый Соответствие);
	Контекст.Вставить("ИсходнаяСтруктураОграничения", Контекст.СтруктураОграничения);
	Контекст.Вставить("ЕстьФункцияПравоДоступаИлиРольДоступна", Ложь);
	
	СтруктураОграничения = Новый Структура(Новый ФиксированнаяСтруктура(Контекст.СтруктураОграничения)); // см. СтруктураОграничения
	Контекст.Вставить("ИмяПрава", "Чтение");
	СтруктураОграничения.ОграничениеЧтения = УпрощенноеУсловиеОграничения(
		СтруктураОграничения.ОграничениеЧтения, Контекст, Истина);
	Контекст.Вставить("ЕстьФункцияПравоДоступаИлиРольДоступнаВОграниченииЧтения",
		Контекст.ЕстьФункцияПравоДоступаИлиРольДоступна);
	Контекст.Вставить("ИмяПрава", "Изменение");
	СтруктураОграничения.ОграничениеИзменения = УпрощенноеУсловиеОграничения(
		СтруктураОграничения.ОграничениеИзменения, Контекст, Истина);
	Контекст.Вставить("СтруктураОграничения", СтруктураОграничения);
	
	ПоляКлючаДоступаПослеУпрощения = Новый Массив; // Массив Из см. НовоеПолеКлючаДоступа
	Для Каждого ОписаниеПоля Из ПоляКлючаДоступаДоУпрощения Цикл
		РодителиПоля = Контекст.ОставшиесяПоляПослеУпрощения.Получить(ОписаниеПоля.Поле); // Массив из см. ОписаниеУзла
		Если РодителиПоля = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		РодителиПоля.Добавить(Новый Структура("Узел", ""));
		ОписаниеПоля.Вставить("Родители", РодителиПоля);
		ПоляКлючаДоступаПослеУпрощения.Добавить(ОписаниеПоля);
	КонецЦикла;
	Контекст.Вставить("ПоляКлючаДоступаПослеУпрощения", ПоляКлючаДоступаПослеУпрощения);
	
	Контекст.Вставить("СвойстваПолейКлючаДоступа", Новый Соответствие);
	Контекст.Вставить("ОбратныйТипПользователя", ?(Контекст.ДляВнешнихПользователей,
		Тип("СправочникСсылка.Пользователи"), Тип("СправочникСсылка.ВнешниеПользователи")));
	Контекст.Вставить("ОбратныйТипГруппыПользователей", ?(Контекст.ДляВнешнихПользователей,
		Тип("СправочникСсылка.ГруппыПользователей"), Тип("СправочникСсылка.ГруппыВнешнихПользователей")));
	ДобавленныеПоля = Новый Соответствие;
	ИменаПолейДляЗапроса = Новый Массив;
	Для Каждого ОписаниеПоля Из ПоляКлючаДоступаПослеУпрощения Цикл
		СвойстваПоля = СвойстваВсехПолей.Получить(ОписаниеПоля.Поле); // см. СвойстваПоля
		СвойстваПоля.Вставить("Чтение",    ОписаниеПоля.Чтение);
		СвойстваПоля.Вставить("Изменение", ОписаниеПоля.Изменение);
		УточнитьСвойстваПоляСравнения(СвойстваПоля, ОписаниеПоля, Контекст);
		НаборПолей = НаборПолейУсловияКогда(СвойстваПоля, ОписаниеПоля, Контекст);
		Для Каждого СвойстваПоля Из НаборПолей Цикл
			ДобавитьСвойстваТиповПоля(СвойстваПоля, ОписаниеПоля, Контекст);
			ОдинаковыеПоля = ДобавленныеПоля.Получить(ВРег(СвойстваПоля.ИмяПоляДляЗапроса));
			Если ОдинаковыеПоля = Неопределено Тогда
				ОдинаковыеПоля = Новый Массив;
				ДобавленныеПоля.Вставить(ВРег(СвойстваПоля.ИмяПоляДляЗапроса), ОдинаковыеПоля);
				ИменаПолейДляЗапроса.Добавить(СвойстваПоля.ИмяПоляДляЗапроса);
			КонецЕсли;
			ОдинаковыеПоля.Добавить(СвойстваПоля);
		КонецЦикла;
	КонецЦикла;
	
	СвойстваПолей = Новый Массив; // Массив из см. СвойстваПоля
	СовмещенныеПоля = Новый Соответствие;
	Для Каждого ИмяПоляДляЗапроса Из ИменаПолейДляЗапроса Цикл
		ОдинаковыеПоля = ДобавленныеПоля.Получить(ВРег(ИмяПоляДляЗапроса));
		ОбработанныеОдинаковыеПоля = Новый Массив;
		Для Каждого СвойстваПоля Из ОдинаковыеПоля Цикл
			СовмещенноеПоле = ОбработанноеСовмещенноеПоле(ОбработанныеОдинаковыеПоля, СвойстваПоля);
			Если СовмещенноеПоле <> Неопределено Тогда
				СовмещенныеПоля.Вставить(СвойстваПоля, СовмещенноеПоле);
				Продолжить;
			КонецЕсли;
			ОбработанныеОдинаковыеПоля.Добавить(СвойстваПоля);
		КонецЦикла;
		Для Каждого СвойстваПоля Из ОбработанныеОдинаковыеПоля Цикл
			СвойстваПолей.Добавить(СвойстваПоля);
		КонецЦикла;
	КонецЦикла;
	
	Для Каждого Описание Из Контекст.СвойстваПолейКлючаДоступа Цикл
		СовмещенноеПоле = СовмещенныеПоля.Получить(Описание.Значение);
		Если СовмещенноеПоле <> Неопределено Тогда
			Контекст.СвойстваПолейКлючаДоступа[Описание.Ключ] = СовмещенноеПоле;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого СвойстваПоля Из СвойстваПолей Цикл
		СвойстваПоля.Вставить("ТипыСохраненияТиповКонфигурации", Новый Массив);
		СвойстваПоля.Вставить("ТипыСохраненияТиповРасширений",   Новый Массив);
		СвойстваПоля.Вставить("ТипыСохраненияТиповПростых",      Новый Массив);
		
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияТипов Цикл
			ОбъектМетаданных = Неопределено;
			ИмяТипа = ИмяТипаНаЯзыкеЗапросов(Тип, ОбъектМетаданных);
			
			Если СтрНайти(ИмяТипа, ".") = 0 Тогда
				СвойстваПоля.ТипыСохраненияТиповПростых.Добавить(Тип);
				
			ИначеЕсли ОбъектМетаданных.РасширениеКонфигурации() = Неопределено Тогда
				СвойстваПоля.ТипыСохраненияТиповКонфигурации.Добавить(Тип);
			Иначе
				СвойстваПоля.ТипыСохраненияТиповРасширений.Добавить(Тип);
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	Контекст.Вставить("СвойстваПолей", СвойстваПолей);
	
КонецПроцедуры

// Для процедуры ЗаполнитьСвойстваПолей.
Функция УпрощенноеУсловиеОграничения(Знач Условие, Контекст, КореньУсловия = Ложь, ДобавитьОставшиесяПоля = Истина)
	
	Если Не ЗначениеЗаполнено(Условие) Тогда
		Возврат Условие;
	КонецЕсли;
	
	ОставшиесяПоляПослеУпрощения = Контекст.ОставшиесяПоляПослеУпрощения;
	Контекст.ОставшиесяПоляПослеУпрощения = Новый Соответствие;
	
	Если Условие.Узел = "Поле" Тогда
		Контекст.ОставшиесяПоляПослеУпрощения.Вставить(Условие, Новый Массив);
		
	ИначеЕсли Условие.Узел = "И"
	      Или Условие.Узел = "Или" Тогда
		
		БезусловныйРезультат = ?(Условие.Узел = "И", Ложь, Истина);
		ЕстьБезусловныйРезультат = Ложь;
		Аргументы = Новый Массив;
		ЕстьФункцияПравоДоступаИлиРольДоступна = Контекст.ЕстьФункцияПравоДоступаИлиРольДоступна;
		Для Каждого ТекущийАргумент Из Условие.Аргументы Цикл
			Аргумент = УпрощенноеУсловиеОграничения(ТекущийАргумент, Контекст);
			Если Аргумент.Узел <> "Константа" Тогда
				Аргументы.Добавить(Аргумент);
			ИначеЕсли Аргумент.Значение = БезусловныйРезультат Тогда
				ЕстьБезусловныйРезультат = Истина;
			КонецЕсли;
		КонецЦикла;
		Если ЕстьБезусловныйРезультат Или Аргументы.Количество() = 0 Тогда
			Контекст.ЕстьФункцияПравоДоступаИлиРольДоступна = ЕстьФункцияПравоДоступаИлиРольДоступна;
			Условие = Новый Структура("Узел, Значение", "Константа",
				?(ЕстьБезусловныйРезультат, БезусловныйРезультат, Истина));
		ИначеЕсли Аргументы.Количество() = 1 Тогда
			Условие = Аргументы[0];
		Иначе
			Условие = Новый Структура("Узел, Аргументы", Условие.Узел, Аргументы);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "Не" Тогда
		Аргумент = УпрощенноеУсловиеОграничения(Условие.Аргумент, Контекст);
		Если Аргумент.Узел = "Константа" Тогда
			Условие = Новый Структура("Узел, Значение", "Константа",
				?(Аргумент.Значение = "Пусто", "Пусто", Не Аргумент.Значение));
		Иначе
			Условие = Новый Структура("Узел, Аргумент", Условие.Узел, Аргумент);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "ДляВсехСтрок"
	      Или Условие.Узел = "ДляОднойИзСтрок"
	      Или Условие.Узел = "ЕстьNull"
	      Или Условие.Узел = "ТипЗначения" Тогда
		
		Аргумент = УпрощенноеУсловиеОграничения(Условие.Аргумент, Контекст);
		Если Аргумент.Узел = "Константа" Тогда
			Условие = Аргумент;
		Иначе
			Условие = Новый Структура("Узел, Аргумент", Условие.Узел, Аргумент);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "="
	      Или Условие.Узел = "<>" Тогда
		
		ПервыйАргумент = УпрощенноеУсловиеОграничения(Условие.ПервыйАргумент, Контекст);
		ВторойАргумент = УпрощенноеУсловиеОграничения(Условие.ВторойАргумент, Контекст);
		
		Если ПервыйАргумент.Узел = "Константа"
		   И ВторойАргумент.Узел = "Константа" Тогда
			
			Условие = Новый Структура("Узел, Значение", "Константа", ?(Условие.Узел = "=",
				ПервыйАргумент.Значение =  ВторойАргумент.Значение,
				ПервыйАргумент.Значение <> ВторойАргумент.Значение));
		Иначе
			Условие = Новый Структура("Узел, ПервыйАргумент, ВторойАргумент",
				Условие.Узел, ПервыйАргумент, ВторойАргумент);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "В" Тогда
		Условие = Новый Структура("Узел, Искомое, Значения",
			Условие.Узел,
			УпрощенноеУсловиеОграничения(Условие.Искомое, Контекст),
			Условие.Значения);
		
	ИначеЕсли Условие.Узел = "Выбор" Тогда
		ВсеЗначенияВыбораИстина = Истина;
		ВсеЗначенияВыбораЛожь   = Истина;
		ВсеЗначенияВыбораПусто  = Истина;
		Выбор = Новый Структура("Узел, Выбор, Когда, Иначе", Условие.Узел, Условие.Выбор, Новый Массив);
		НовоеУсловиеИначе = Неопределено;
		Для Каждого Когда Из Условие.Когда Цикл
			Если Условие.Выбор = Неопределено Тогда
				УсловиеКогда = УпрощенноеУсловиеОграничения(Когда.Условие, Контекст);
				Если УсловиеКогда.Узел = "Константа" Тогда
					Если УсловиеКогда.Значение Тогда
						НовоеУсловиеИначе = ?(НовоеУсловиеИначе = Неопределено, Когда.Значение, НовоеУсловиеИначе);
					КонецЕсли;
					ЗначениеКогда = УпрощенноеУсловиеОграничения(Когда.Значение, Контекст, , Ложь);
					Продолжить;
				КонецЕсли;
			Иначе
				УсловиеКогда = Когда.Условие;
			КонецЕсли;
			ЗначениеКогда = УпрощенноеУсловиеОграничения(Когда.Значение, Контекст);
			ОбработатьУпрощенноеЗначениеВыбора(ЗначениеКогда,
				ВсеЗначенияВыбораИстина, ВсеЗначенияВыбораЛожь, ВсеЗначенияВыбораПусто);
			СтруктураКогда = Новый Структура("Условие, Значение", УсловиеКогда, ЗначениеКогда);
			Выбор.Когда.Добавить(СтруктураКогда);
		КонецЦикла;
		УсловиеИначе = ?(НовоеУсловиеИначе = Неопределено, Условие.Иначе, НовоеУсловиеИначе);
		Выбор.Иначе = УпрощенноеУсловиеОграничения(УсловиеИначе, Контекст);
		ОбработатьУпрощенноеЗначениеВыбора(Выбор.Иначе,
			ВсеЗначенияВыбораИстина, ВсеЗначенияВыбораЛожь, ВсеЗначенияВыбораПусто);
		Если ВсеЗначенияВыбораИстина Или ВсеЗначенияВыбораЛожь Или ВсеЗначенияВыбораПусто Тогда
			Условие = Новый Структура("Узел, Значение", "Константа",
				?(ВсеЗначенияВыбораПусто, "Пусто", ВсеЗначенияВыбораИстина));
		ИначеЕсли Выбор.Когда.Количество() = 0 Тогда
			Условие = Выбор.Иначе;
		Иначе
			Условие = Выбор;
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "ЗначениеРазрешено"
	      Или Условие.Узел = "ЧтениеОбъектаРазрешено"
	      Или Условие.Узел = "ИзменениеОбъектаРазрешено" Тогда
		
		СвойстваПоля = Контекст.СвойстваВсехПолей.Получить(Условие.Поле);
		Если СвойстваПоля = Неопределено Тогда
			ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'При упрощении условия ограничения права %1
				           |списка ""%2""
				           |свойства поля не определены для узла ""%3"".'"),
				Контекст.ИмяПрава,
				Контекст.Список,
				Условие.Узел);
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
		ТипыКонечногоПоля = СвойстваПоля.ТипКонечногоПоля.Типы();
		
		Если Условие.Узел = "ЗначениеРазрешено" Тогда
			Результат = РезультатФункцииЗначениеРазрешено(Условие, ТипыКонечногоПоля, Контекст);
		Иначе
			Результат = РезультатФункцииПравоОбъектаРазрешено(Условие, ТипыКонечногоПоля, Контекст);
		КонецЕсли;
		
		Если Результат <> Неопределено Тогда
			Условие = Новый Структура("Узел, Значение", "Константа", Результат);
		Иначе
			Контекст.ОставшиесяПоляПослеУпрощения.Вставить(Условие.Поле, Новый Массив);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "ЭтоАвторизованныйПользователь"
	      Или Условие.Узел = "ЧтениеСпискаРазрешено"
	      Или Условие.Узел = "ИзменениеСпискаРазрешено" Тогда
		
		Контекст.ОставшиесяПоляПослеУпрощения.Вставить(Условие.Поле, Новый Массив);
		
	ИначеЕсли Условие.Узел = "ПравоДоступа"
	      Или Условие.Узел = "РольДоступна" Тогда
		
		Контекст.ЕстьФункцияПравоДоступаИлиРольДоступна = Истина;
	
	ИначеЕсли Условие.Узел <> "Константа"
	        И Условие.Узел <> "Значение"
	        И Условие.Узел <> "Тип" Тогда
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'При упрощении условия ограничения права %1
			           |списка ""%2""
			           |узел не поддерживается ""%3"".'"),
			Контекст.ИмяПрава,
			Контекст.Список,
			Условие.Узел);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Если Условие.Узел = "Константа" Тогда
		Если КореньУсловия И Условие.Значение = "Пусто" Тогда
			Условие.Значение = Истина;
		КонецЕсли;
	ИначеЕсли ДобавитьОставшиесяПоля Тогда
		Для Каждого КлючИЗначение Из Контекст.ОставшиесяПоляПослеУпрощения Цикл
			Если Условие.Узел <> "Поле" Тогда
				РодителиПоля = КлючИЗначение.Значение; // Массив Из см. ОписаниеУзла
				РодителиПоля.Добавить(Условие);
			КонецЕсли;
			ОставшиесяПоляПослеУпрощения.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);
		КонецЦикла;
	КонецЕсли;
	Контекст.ОставшиесяПоляПослеУпрощения = ОставшиесяПоляПослеУпрощения;
	
	Возврат Условие;
	
КонецФункции

// Для функции УпрощенноеУсловиеОграничения.
Процедура ОбработатьУпрощенноеЗначениеВыбора(Условие,
			ВсеЗначенияВыбораИстина, ВсеЗначенияВыбораЛожь, ВсеЗначенияВыбораПусто)
	
	Если Условие.Узел <> "Константа" Тогда
		ВсеЗначенияВыбораИстина = Ложь;
		ВсеЗначенияВыбораЛожь   = Ложь;
		ВсеЗначенияВыбораПусто  = Ложь;
		
	ИначеЕсли Условие.Значение = "Пусто" Тогда
		ВсеЗначенияВыбораИстина = Ложь;
		ВсеЗначенияВыбораЛожь   = Ложь;
		
	ИначеЕсли Условие.Значение Тогда
		ВсеЗначенияВыбораЛожь  = Ложь;
		ВсеЗначенияВыбораПусто = Ложь;
	Иначе
		ВсеЗначенияВыбораИстина = Ложь;
		ВсеЗначенияВыбораПусто  = Ложь;
	КонецЕсли;
	
КонецПроцедуры

// Для функции УпрощенноеУсловиеОграничения.
Функция РезультатФункцииЗначениеРазрешено(Условие, ТипыКонечногоПоля, Контекст)

	СвойстваВидовДоступаПоТипам = Контекст.СвойстваВидовДоступа.ПоТипамГруппИЗначений;
	ОтключеноКакЛожь = УточнениеТипа(Условие, "Отключено") = "Ложь";
	
	РезультатВсегдаЛожь   = Истина;
	РезультатВсегдаИстина = Истина;
	ЕстьРезультатПусто    = Ложь;
	
	Для Каждого Тип Из ТипыКонечногоПоля Цикл
		ИмяТипа = ИмяТипаНаЯзыкеЗапросов(Тип);
		
		Уточнение = УточнениеТипа(Условие, ИмяТипа);
		Если Уточнение = "Истина" Тогда
			РезультатВсегдаЛожь = Ложь;
			Продолжить;
		ИначеЕсли Уточнение = "Ложь" Тогда
			РезультатВсегдаИстина = Ложь;
			Продолжить;
		КонецЕсли;
		
		Если Не ТипПроверяется(Условие, ИмяТипа) Тогда
			РезультатВсегдаИстина = Ложь;
			Продолжить;
		КонецЕсли;
		
		СвойстваВидаДоступа = СвойстваВидовДоступаПоТипам.Получить(Тип);
		Если СвойстваВидаДоступа = Неопределено Тогда
			РезультатВсегдаЛожь = Ложь;
			Продолжить;
		КонецЕсли;
		
		Если Не ТипЗначенийДоступаИспользуется(Контекст, СвойстваВидаДоступа.ТипЗначений) Тогда
			Если Контекст.НеиспользуемыеТипыЗначенийДоступа.Найти(СвойстваВидаДоступа.ТипЗначений) = Неопределено Тогда
				Контекст.НеиспользуемыеТипыЗначенийДоступа.Добавить(СвойстваВидаДоступа.ТипЗначений);
			КонецЕсли;
			Если ОтключеноКакЛожь Тогда
				РезультатВсегдаИстина = Ложь;
				ЕстьРезультатПусто = Истина;
			Иначе
				РезультатВсегдаЛожь = Ложь;
			КонецЕсли;
		Иначе
			РезультатВсегдаИстина = Ложь;
			РезультатВсегдаЛожь = Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Если РезультатВсегдаИстина Тогда
		Возврат Истина;
	ИначеЕсли РезультатВсегдаЛожь Тогда
		Если ЕстьРезультатПусто Тогда
			Возврат "Пусто";
		Иначе
			Возврат Ложь;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Для функции РезультатФункцииЗначениеРазрешено и процедуры ДобавитьСвойстваТиповПоля.
Функция ТипЗначенийДоступаИспользуется(Контекст, ТипЗначений)
	
	ИспользуемыеТипыЗначений = Контекст.ИспользуемыеТипыЗначений.ПоТаблицам.Получить(Контекст.Список);
	
	Если ИспользуемыеТипыЗначений = Неопределено Тогда
		Если Контекст.ИспользуемыеТипыЗначений.ПолноеИмяТаблицы = Контекст.Список
		 Или Не ЗначениеЗаполнено(Контекст.ИспользуемыеТипыЗначений.ПолноеИмяТаблицы) Тогда
			Возврат Ложь;
		КонецЕсли;
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ошибка вызова функции %1 общего модуля %2:
			           |Для таблицы %3
			           |не заполнены используемые типы значений доступа.'"),
			"ТипЗначенийДоступаИспользуется",
			"УправлениеДоступомСлужебный",
			Контекст.Список);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Возврат ИспользуемыеТипыЗначений.Получить(ТипЗначений) <> Неопределено;
	
КонецФункции

// Для функции УпрощенноеУсловиеОграничения.
Функция РезультатФункцииПравоОбъектаРазрешено(Условие, ТипыКонечногоПоля, Контекст)

	Если Контекст.ОграничениеДоступаВключено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	РезультатВсегдаИстина = Истина;
	
	Для Каждого Тип Из ТипыКонечногоПоля Цикл
		ИмяТипа = ИмяТипаНаЯзыкеЗапросов(Тип);
		
		Уточнение = УточнениеТипа(Условие, ИмяТипа);
		Если Уточнение = "Истина" Тогда
			Продолжить;
		ИначеЕсли Уточнение = "Ложь" Тогда
			РезультатВсегдаИстина = Ложь;
			Прервать;
		КонецЕсли;
		
		Если Не ТипПроверяется(Условие, ИмяТипа) Тогда
			РезультатВсегдаИстина = Ложь;
			Прервать;
		КонецЕсли;
		
		Значение = Контекст.ТипыВладельцевНастроекПрав.Получить(Тип);
		Если Значение = Неопределено
		 Или ВРег(Контекст.Список) <> ВРег(Значение[0].ВладелецПрав) Тогда
		
			РезультатВсегдаИстина = Ложь;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если РезультатВсегдаИстина Тогда
		Возврат Истина;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Для процедуры ЗаполнитьСвойстваПолей.
Функция ОбработанноеСовмещенноеПоле(ОбработанныеОдинаковыеПоля, СвойстваПоля)
	
	Для Каждого Поле Из ОбработанныеОдинаковыеПоля Цикл
		Совместимо = Истина;
		Если Поле.ПсевдонимТаблицы = "ТекущийСписок"
		   И Поле.НесколькоГруппЗначений <> СвойстваПоля.НесколькоГруппЗначений Тогда
			Продолжить;
		КонецЕсли;
		// По пустой ссылке на ключ доступа или значению Null невозможно определить тип значения,
		// поэтому сохранение ключа доступа несовместимо с остальными вариантами.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияКлючейДоступа Цикл
			Если Поле.ТипыСохраненияЗначений.Найти(Тип)        <> Неопределено
			 Или Поле.ТипыСохраненияГруппЗначений.Найти(Тип)   <> Неопределено
			 Или Поле.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или Поле.ТипыСохраненияТипаЗапрещенный.Найти(Тип) <> Неопределено
			 Или Поле.ТипыСохраненияТипаРазрешенный.Найти(Тип) <> Неопределено Тогда
				Совместимо = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Для Каждого Тип Из Поле.ТипыСохраненияКлючейДоступа Цикл
			Если СвойстваПоля.ТипыСохраненияЗначений.Найти(Тип)        <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияГруппЗначений.Найти(Тип)   <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияТипаЗапрещенный.Найти(Тип) <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияТипаРазрешенный.Найти(Тип) <> Неопределено Тогда
				Совместимо = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Не Совместимо Тогда
			Продолжить;
		КонецЕсли;
		// По значению Null вместо группы значений невозможно определить тип значения,
		// поэтому сохранение группы значений несовместимо с остальными вариантами.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияГруппЗначений Цикл
			Если Поле.ТипыСохраненияЗначений.Найти(Тип)        <> Неопределено
			 Или Поле.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или Поле.ТипыСохраненияТипаЗапрещенный.Найти(Тип) <> Неопределено
			 Или Поле.ТипыСохраненияТипаРазрешенный.Найти(Тип) <> Неопределено Тогда
				Совместимо = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Для Каждого Тип Из Поле.ТипыСохраненияГруппЗначений Цикл
			Если СвойстваПоля.ТипыСохраненияЗначений.Найти(Тип)        <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияТипаЗапрещенный.Найти(Тип) <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияТипаРазрешенный.Найти(Тип) <> Неопределено Тогда
				Совместимо = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		// Вместо простых типов сохраняется значения ТипРазрешенный или ТипЗапрещенный,
		// поэтому сохранение запрещенного простого типа несовместимо с остальными вариантами.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияТипаЗапрещенный Цикл
			Если Не ЭтоПростойТип(Тип) Тогда
				Продолжить;
			КонецЕсли;
			Если Поле.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или Поле.ТипыСохраненияТипаРазрешенный.Найти(Тип) <> Неопределено Тогда
				Совместимо = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Для Каждого Тип Из Поле.ТипыСохраненияТипаЗапрещенный Цикл
			Если Не ЭтоПростойТип(Тип) Тогда
				Продолжить;
			КонецЕсли;
			Если СвойстваПоля.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или СвойстваПоля.ТипыСохраненияТипаРазрешенный.Найти(Тип) <> Неопределено Тогда
				Совместимо = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Не Совместимо Тогда
			Продолжить;
		КонецЕсли;
		// Объединение уточнений специальных значений.
		Поле.ЕстьУточнениеNull =
			Поле.ЕстьУточнениеNull Или СвойстваПоля.ЕстьУточнениеNull;
		Поле.ЕстьУточнениеНеопределено =
			Поле.ЕстьУточнениеНеопределено Или СвойстваПоля.ЕстьУточнениеНеопределено;
		Поле.ЕстьТипВедущегоСписка =
			Поле.ЕстьТипВедущегоСписка Или СвойстваПоля.ЕстьТипВедущегоСписка;
		Поле.ЕстьТипВладельцаНастроекПрав =
			Поле.ЕстьТипВладельцаНастроекПрав Или СвойстваПоля.ЕстьТипВладельцаНастроекПрав;
		Поле.ЕстьПроверкаАвторизованногоПользователя =
			Поле.ЕстьПроверкаАвторизованногоПользователя Или СвойстваПоля.ЕстьПроверкаАвторизованногоПользователя;
		// Объединение типов сохранения пустой ссылки.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияПустойСсылки Цикл
			Если Поле.ТипыСохраненияПустойСсылки.Найти(Тип) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Поле.ТипыСохраненияПустойСсылки.Добавить(Тип);
		КонецЦикла;
		// Объединение типов сохранения значения ТипРазрешенный.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияТипаРазрешенный Цикл
			Если Поле.ТипыСохраненияЗначений.Найти(Тип)        <> Неопределено
			 Или Поле.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или Поле.ТипыСохраненияТипаРазрешенный.Найти(Тип) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Индекс = Поле.ТипыСохраненияТипаЗапрещенный.Найти(Тип);
			Если Индекс <> Неопределено Тогда
				Поле.ТипыСохраненияТипаЗапрещенный.Удалить(Индекс);
				Поле.ТипыСохраненияТипов.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			Поле.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
		КонецЦикла;
		// Объединение типов сохранения значения ТипЗапрещенный.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияТипаЗапрещенный Цикл
			Если Поле.ТипыСохраненияЗначений.Найти(Тип)        <> Неопределено
			 Или Поле.ТипыСохраненияТипов.Найти(Тип)           <> Неопределено
			 Или Поле.ТипыСохраненияТипаЗапрещенный.Найти(Тип) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Индекс = Поле.ТипыСохраненияТипаРазрешенный.Найти(Тип);
			Если Индекс <> Неопределено Тогда
				Поле.ТипыСохраненияТипаРазрешенный.Удалить(Индекс);
				Поле.ТипыСохраненияТипов.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			Поле.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
		КонецЦикла;
		// Объединение типов сохранения типа значения.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияТипов Цикл
			Если Поле.ТипыСохраненияЗначений.Найти(Тип) <> Неопределено
			 Или Поле.ТипыСохраненияТипов.Найти(Тип)    <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Индекс = Поле.ТипыСохраненияТипаЗапрещенный.Найти(Тип);
			Если Индекс <> Неопределено Тогда
				Поле.ТипыСохраненияТипаЗапрещенный.Удалить(Индекс);
			КонецЕсли;
			Индекс = Поле.ТипыСохраненияТипаРазрешенный.Найти(Тип);
			Если Индекс <> Неопределено Тогда
				Поле.ТипыСохраненияТипаРазрешенный.Удалить(Индекс);
			КонецЕсли;
			Поле.ТипыСохраненияТипов.Добавить(Тип);
		КонецЦикла;
		// Объединение типов сохранения значения.
		Для Каждого Тип Из СвойстваПоля.ТипыСохраненияЗначений Цикл
			Если Поле.ТипыСохраненияЗначений.Найти(Тип) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Индекс = Поле.ТипыСохраненияТипов.Найти(Тип);
			Если Индекс <> Неопределено Тогда
				Поле.ТипыСохраненияТипов.Удалить(Индекс);
			КонецЕсли;
			Индекс = Поле.ТипыСохраненияТипаЗапрещенный.Найти(Тип);
			Если Индекс <> Неопределено Тогда
				Поле.ТипыСохраненияТипаЗапрещенный.Удалить(Индекс);
			КонецЕсли;
			Индекс = Поле.ТипыСохраненияТипаРазрешенный.Найти(Тип);
			Если Индекс <> Неопределено Тогда
				Поле.ТипыСохраненияТипаРазрешенный.Удалить(Индекс);
			КонецЕсли;
			Поле.ТипыСохраненияЗначений.Добавить(Тип);
		КонецЦикла;
		// Объединение неиспользуемых типов значений доступа.
		Для Каждого Тип Из СвойстваПоля.НеиспользуемыеТипыЗначенийДоступа Цикл
			Если Поле.НеиспользуемыеТипыЗначенийДоступа.Найти(Тип) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Поле.НеиспользуемыеТипыЗначенийДоступа.Добавить(Тип);
		КонецЦикла;
		// Объединение используемых типов значений доступа.
		Для Каждого Тип Из СвойстваПоля.ИспользуемыеТипыЗначенийДоступа Цикл
			Если Поле.ИспользуемыеТипыЗначенийДоступа.Найти(Тип) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Поле.ИспользуемыеТипыЗначенийДоступа.Добавить(Тип);
		КонецЦикла;
		Поле.Чтение = Поле.Чтение Или СвойстваПоля.Чтение;
		Если Поле.ТипыСтрокой <> СвойстваПоля.ТипыСтрокой Тогда
			Поле.ТипыСтрокой = Поле.ТипыСтрокой + Символы.ПС + СвойстваПоля.ТипыСтрокой;
		КонецЕсли;
		Возврат Поле;
	КонецЦикла;
	
	Возврат Неопределено;
	
КонецФункции

// Для процедуры ЗаполнитьСвойстваПолей.
Процедура УточнитьСвойстваПоляСравнения(Свойства, ОписаниеПоля, Контекст)
	
	Контекст.СвойстваПолейКлючаДоступа.Вставить(ОписаниеПоля.Поле, Свойства);
	
	Родитель = ОписаниеПоля.Родители[0];
	
	Узлы = ",ТипЗначения,=,<>,В,ЕстьNull,"; // В ключ сохраняется результат сравнения.
	Если СтрНайти(Узлы, "," + Родитель.Узел + ",") = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если Родитель.Узел = "ТипЗначения" Тогда
		РодительУзлаСравнения = ОписаниеПоля.Родители[1];
		
		Если РодительУзлаСравнения.ВторойАргумент = Родитель Тогда
			УзелТип         = РодительУзлаСравнения.ПервыйАргумент; // См. ОписаниеУзла
			УзелТипЗначения = РодительУзлаСравнения.ВторойАргумент;
		Иначе
			УзелТип         = РодительУзлаСравнения.ВторойАргумент; // См. ОписаниеУзла
			УзелТипЗначения = РодительУзлаСравнения.ПервыйАргумент;
		КонецЕсли;
		Контекст.СвойстваПолейКлючаДоступа.Вставить(УзелТипЗначения, Свойства);
		
		ВыражениеТипаЗначения = "ТИПЗНАЧЕНИЯ(" + Свойства.ИмяПоляДляЗапроса + ")"; // @query-part-1
		ВыражениеТипа = "ТИП(" + УзелТип.Имя + ")"; // @query-part-1
		ОднойСтрокой = СтрДлина(ВыражениеТипаЗначения) + СтрДлина(ВыражениеТипа) < 60;
		
		Свойства.ИмяПоляДляЗапроса = ВыражениеТипаЗначения + ?(ОднойСтрокой, " ", "
		|			") + РодительУзлаСравнения.Узел + " " + ВыражениеТипа;
		
	ИначеЕсли Родитель.Узел = "="
	      Или Родитель.Узел = "<>" Тогда
		
		УзелЗначениеИлиКонстанта = ?(Родитель.ВторойАргумент = ОписаниеПоля.Поле,
			Родитель.ПервыйАргумент, Родитель.ВторойАргумент);
		
		ВыражениеУзла = ВыражениеУзлаЗначениеИлиКонстанта(УзелЗначениеИлиКонстанта);
		ОднойСтрокой = СтрДлина(Свойства.ИмяПоляДляЗапроса) + СтрДлина(ВыражениеУзла) < 60;
		
		Свойства.ИмяПоляДляЗапроса = Свойства.ИмяПоляДляЗапроса + ?(ОднойСтрокой, " ", "
		|			") + Родитель.Узел + " " + ВыражениеУзла;
		
	ИначеЕсли Родитель.Узел = "В" Тогда
		
		СписокЗначений = "";
		Для Каждого УзелЗначениеИлиКонстанта Из Родитель.Значения Цикл
			СписокЗначений = СписокЗначений + ?(СписокЗначений = "", "", ",");
			ПоследняяСтрока = СтрПолучитьСтроку(СписокЗначений, СтрЧислоСтрок(СписокЗначений));
			Если СтрДлина(ПоследняяСтрока) > 40 Тогда
				СписокЗначений = СписокЗначений + "
				|			";
			Иначе
				СписокЗначений = СписокЗначений + " ";
			КонецЕсли;
			СписокЗначений = СписокЗначений
				+ ВыражениеУзлаЗначениеИлиКонстанта(УзелЗначениеИлиКонстанта);
		КонецЦикла;
		Свойства.ИмяПоляДляЗапроса = Свойства.ИмяПоляДляЗапроса + "
		|	В (" + СписокЗначений + ")";
	
	Иначе // Родитель.Узел = "ЕстьNull".
		Свойства.ИмяПоляДляЗапроса = Свойства.ИмяПоляДляЗапроса + " ЕСТЬ NULL";
		Свойства.Вставить("ПроверкаЕстьNull");
	КонецЕсли;
	
	Свойства.ТипКонечногоПоля = Новый ОписаниеТипов("Булево");
	
КонецПроцедуры

// Для процедуры ЗаполнитьСвойстваПолей.
Функция НаборПолейУсловияКогда(ИсходныеСвойстваПоля, ОписаниеПоля, Контекст)
	
	Родитель = ОписаниеПоля.Родители[0];
	НаборПолей = Новый Массив;
	
	Если Родитель.Узел <> "Выбор"
	 Или Родитель.Выбор = Неопределено Тогда
		
		НаборПолей.Добавить(ИсходныеСвойстваПоля);
		Возврат НаборПолей;
	КонецЕсли;
	
	// Для "ВЫБОР <Поле> КОГДА <Значение>" в ключ сохраняется
	// результат сравнения "<Поле> = <Значение>".
	
	Для Каждого ОписаниеКогда Из Родитель.Когда Цикл
		ФиксированныеСвойства = Новый ФиксированнаяСтруктура(ИсходныеСвойстваПоля);
		Свойства = Новый Структура(ФиксированныеСвойства);
		
		ВыражениеУзла = ВыражениеУзлаЗначениеИлиКонстанта(ОписаниеКогда.Условие);
		ОднойСтрокой = СтрДлина(Свойства.ИмяПоляДляЗапроса) + СтрДлина(ВыражениеУзла) < 60;
		
		Свойства.ИмяПоляДляЗапроса = Свойства.ИмяПоляДляЗапроса + ?(ОднойСтрокой, " = ", "
		|			= ") + ВыражениеУзла;
		
		Свойства.ТипКонечногоПоля = Новый ОписаниеТипов("Булево");
		НаборПолей.Добавить(Свойства);
		
		Контекст.СвойстваПолейКлючаДоступа.Вставить(ОписаниеКогда.Условие, Свойства);
	КонецЦикла;
	
	Возврат НаборПолей;
	
КонецФункции

// Для функций СвойстваПоля, УточнитьСвойстваПоляСравнения, НаборПолейУсловияКогда.
Функция БезЗначенияNull(СвойстваПоля, ВГруппеОднаДополнительнаяТаблицаСПолями)
	
	Возврат СтрЧислоВхождений(СвойстваПоля.ИмяПоляДляЗапроса, ".") = 1
	      И Не СвойстваПоля.Свойство("ПолеСодержитNull")
	      И (    СтрНачинаетсяС(СвойстваПоля.ИмяПоляДляЗапроса, "ТекущийСписок")
	         Или ВГруппеОднаДополнительнаяТаблицаСПолями);
	
КонецФункции

// Для процедуры ЗаполнитьСвойстваПолей.
//
// Параметры:
//   Свойства     - см. СвойстваПоля
//   ОписаниеПоля - см. НовоеПолеКлючаДоступа
//   Контекст     - см. КонтекстПараметровПоСтруктуреОграничения
//
Процедура ДобавитьСвойстваТиповПоля(Свойства, ОписаниеПоля, Контекст)
	
	Свойства.Вставить("ТипыСохраненияКлючейДоступа",               Новый Массив);
	Свойства.Вставить("ТипыСохраненияГруппЗначений",               Новый Массив);
	Свойства.Вставить("ТипыСохраненияЗначений",                    Новый Массив);
	Свойства.Вставить("ТипыСохраненияПустойСсылки",                Новый Массив);
	Свойства.Вставить("ТипыСохраненияТипов",                       Новый Массив);
	Свойства.Вставить("ТипыСохраненияТипаЗапрещенный",             Новый Массив);
	Свойства.Вставить("ТипыСохраненияТипаРазрешенный",             Новый Массив);
	Свойства.Вставить("НеиспользуемыеТипыЗначенийДоступа",         Новый Массив);
	Свойства.Вставить("ИспользуемыеТипыЗначенийДоступа",           Новый Массив);
	Свойства.Вставить("НесколькоГруппЗначений",                    Ложь);
	Свойства.Вставить("ЭтоСписокЗначенийДоступаСГруппамиЗначений", Ложь);
	Свойства.Вставить("ЕстьУточнениеNull",                         Ложь);
	Свойства.Вставить("ЕстьУточнениеНеопределено",                 Ложь);
	Свойства.Вставить("ЕстьТипВедущегоСписка",                     Ложь);
	Свойства.Вставить("ЕстьТипВладельцаНастроекПрав",              Ложь);
	Свойства.Вставить("ЕстьПроверкаАвторизованногоПользователя",   Ложь);
	
	Родитель = ОписаниеПоля.Родители[0];
	
	Если Родитель.Узел = "ЧтениеОбъектаРазрешено"
	 Или Родитель.Узел = "ИзменениеОбъектаРазрешено"
	 Или Родитель.Узел = "ЧтениеСпискаРазрешено"
	 Или Родитель.Узел = "ИзменениеСпискаРазрешено"
	 Или Родитель.Узел = "ЗначениеРазрешено"
	 Или Родитель.Узел = "ЭтоАвторизованныйПользователь" Тогда
		
		ЕстьУточнениеПустойСсылки          = ТипУточнен(Родитель, "ПустаяСсылка");
		Свойства.ЕстьУточнениеNull         = ТипУточнен(Родитель, "Null") Или Свойства.Свойство("ЕстьВыразить");
		Свойства.ЕстьУточнениеНеопределено = ТипУточнен(Родитель, "Неопределено");
	КонецЕсли;
	
	Если Родитель.Узел = "ЧтениеОбъектаРазрешено"
	 Или Родитель.Узел = "ИзменениеОбъектаРазрешено"
	 Или Родитель.Узел = "ЧтениеСпискаРазрешено"
	 Или Родитель.Узел = "ИзменениеСпискаРазрешено" Тогда
		
		Для Каждого Тип Из Свойства.ТипКонечногоПоля.Типы() Цикл
			ИмяТипа = ИмяТипаНаЯзыкеЗапросов(Тип);
			
			Уточнение = УточнениеТипа(Родитель, ИмяТипа);
			Если Уточнение = "Истина" Тогда
				Свойства.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
				Продолжить;
			ИначеЕсли Уточнение = "Ложь" Тогда
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если Не ТипПроверяется(Родитель, ИмяТипа) Тогда
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если ЭтоПростойТип(Тип) Тогда
				Свойства.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если Тип = Тип("СправочникСсылка.ИдентификаторыОбъектовМетаданных")
			 Или Тип = Тип("СправочникСсылка.ИдентификаторыОбъектовРасширений") Тогда
				Свойства.ТипыСохраненияЗначений.Добавить(Тип);
				Свойства.ЕстьТипВедущегоСписка = Истина;
				Продолжить;
			КонецЕсли;
			
			Если Родитель.Узел = "ЧтениеСпискаРазрешено"
			 Или Родитель.Узел = "ИзменениеСпискаРазрешено" Тогда
				
				Свойства.ТипыСохраненияТипов.Добавить(Тип);
				Свойства.ЕстьТипВедущегоСписка = Истина;
				Продолжить;
			КонецЕсли;
			
			Если Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей <> Неопределено
			   И Не Контекст.ИспользуетсяОграничениеПоВладельцу
			   И (    Контекст.СпискиСОграничением.Получить(ИмяТипа) = Неопределено
			      Или Контекст.СпискиСОтключеннымОграничением.Получить(ИмяТипа) <> Неопределено) Тогда
				
				Свойства.ТипыСохраненияТипов.Добавить(Тип);
				Свойства.ЕстьТипВедущегоСписка = Истина;
				Продолжить;
			КонецЕсли;
			
			Если ЕстьУточнениеПустойСсылки Тогда
				Свойства.ТипыСохраненияПустойСсылки.Добавить(Тип);
			КонецЕсли;
			
			ИмяВедущегоПрава = ?(Родитель.Узел = "ЧтениеОбъектаРазрешено", "Чтение", "Изменение");
			
			Значение = Контекст.ТипыВладельцевНастроекПрав.Получить(Тип);
			Если Значение <> Неопределено
			   И ВРег(Контекст.Список) = ВРег(Значение[0].ВладелецПрав) Тогда
				
				Свойства.ТипыСохраненияЗначений.Добавить(Тип);
				Свойства.ЕстьТипВладельцаНастроекПрав = Истина;
				ВидОграничения = "НастройкиПрав." + ИмяТипа + "." + ИмяВедущегоПрава;
				ДобавитьВидОграниченияПрав(ВидОграничения, Свойства, Контекст, Истина);
				Продолжить;
			КонецЕсли;
			
			Свойства.ТипыСохраненияКлючейДоступа.Добавить(Тип);
			Если Контекст.СпискиСЗаписьюКлючейДляЗависимыхСписковБезКлючей = Неопределено
			 Или (  Контекст.СпискиСОграничением.Получить(ИмяТипа) <> Неопределено
			      И Контекст.СпискиСОтключеннымОграничением.Получить(ИмяТипа) = Неопределено) Тогда
			   
				ДобавитьВедущийСписокПоПолюСсылка(Контекст.ВедущиеСпискиПоКлючамДоступа,
					ИмяТипа, ОписаниеПоля, Свойства, Контекст);
				
				ВидОграничения = "Объект." + ИмяТипа + "." + ИмяВедущегоПрава;
				ДобавитьВидОграниченияПрав(ВидОграничения, Свойства, Контекст, Истина);
			КонецЕсли;
		КонецЦикла;
		
		Возврат;
	КонецЕсли;
	
	Если Родитель.Узел = "ЗначениеРазрешено" Тогда
		СвойстваВидовДоступа = Контекст.СвойстваВидовДоступа;
		ОтключеноКакЛожь = УточнениеТипа(Родитель, "Отключено") = "Ложь";
		
		Для Каждого Тип Из Свойства.ТипКонечногоПоля.Типы() Цикл
			ИмяТипа = ИмяТипаНаЯзыкеЗапросов(Тип);
			
			Уточнение = УточнениеТипа(Родитель, ИмяТипа);
			Если Уточнение = "Истина" Тогда
				Свойства.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
				Продолжить;
			ИначеЕсли Уточнение = "Ложь" Тогда
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если Не ТипПроверяется(Родитель, ИмяТипа) Тогда
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			СвойстваВидаДоступа = СвойстваВидовДоступа.ПоТипамГруппИЗначений.Получить(Тип); // См. СвойстваВидаДоступа
			Если СвойстваВидаДоступа = Неопределено Тогда
				Свойства.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если Тип = Контекст.ОбратныйТипПользователя
			 Или Тип = Контекст.ОбратныйТипГруппыПользователей Тогда
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			ДобавитьВидОграниченияПрав(СвойстваВидаДоступа.Имя, Свойства, Контекст);
			
			Если Не ТипЗначенийДоступаИспользуется(Контекст, СвойстваВидаДоступа.ТипЗначений) Тогда
				Свойства.НеиспользуемыеТипыЗначенийДоступа.Добавить(СвойстваВидаДоступа.ТипЗначений);
				
				Если ОтключеноКакЛожь Тогда
					Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Иначе
					Свойства.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
				КонецЕсли;
				Продолжить;
			КонецЕсли;
			
			Свойства.ИспользуемыеТипыЗначенийДоступа.Добавить(СвойстваВидаДоступа.ТипЗначений);
			
			Если СвойстваВидовДоступа.ПоТипамЗначений.Получить(Тип) = Неопределено Тогда
				// Тип группы значений доступа.
				Если Контекст.Список = Метаданные.НайтиПоТипу(Тип).ПолноеИмя()
				 Или Контекст.ТипыПользователя.Найти(Тип) <> Неопределено Тогда
					
					Свойства.ТипыСохраненияЗначений.Добавить(Тип);
				Иначе
					Свойства.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
				КонецЕсли;
				Продолжить;
			КонецЕсли;
			
			Если ЕстьУточнениеПустойСсылки Тогда
				Свойства.ТипыСохраненияПустойСсылки.Добавить(Тип);
			КонецЕсли;
			
			Если СвойстваВидовДоступа.ТипыЗначенийДоступаСГруппами.Получить(Тип) = Неопределено
			 Или Контекст.ТипыПользователя.Найти(Тип) <> Неопределено Тогда
				
				Свойства.ТипыСохраненияЗначений.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			Свойства.ТипыСохраненияГруппЗначений.Добавить(Тип);
			
			Если Контекст.Список = ИмяТипа
			   И ВРег(Свойства.ИмяПоляДляЗапроса) = ВРег("ТекущийСписок.Ссылка") Тогда // @query-part-1
				
				Свойства.ЭтоСписокЗначенийДоступаСГруппамиЗначений = Истина;
			Иначе
				ДобавитьВедущийСписокПоПолюСсылка(Контекст.ВедущиеСпискиПоЗначениямСГруппами,
					ИмяТипа, ОписаниеПоля, Свойства, Контекст);
			КонецЕсли;
			
			Если Не СвойстваВидаДоступа.НесколькоГруппЗначений Тогда
				Продолжить;
			КонецЕсли;
			Свойства.НесколькоГруппЗначений = Истина;
		КонецЦикла;
		
		Возврат;
	КонецЕсли;
	
	Если Родитель.Узел = "ЭтоАвторизованныйПользователь" Тогда
		Свойства.ЕстьПроверкаАвторизованногоПользователя = Истина;
		
		Для Каждого Тип Из Свойства.ТипКонечногоПоля.Типы() Цикл
			ИмяТипа = ИмяТипаНаЯзыкеЗапросов(Тип);
			
			Уточнение = УточнениеТипа(Родитель, ИмяТипа);
			Если Уточнение = "Истина" Тогда
				Свойства.ТипыСохраненияТипаРазрешенный.Добавить(Тип);
				Продолжить;
			ИначеЕсли Уточнение = "Ложь" Тогда
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если Не ТипПроверяется(Родитель, ИмяТипа) Тогда
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если Тип <> Тип("СправочникСсылка.Пользователи")
			   И Тип <> Тип("СправочникСсылка.ВнешниеПользователи") Тогда
				
				Свойства.ТипыСохраненияТипаЗапрещенный.Добавить(Тип);
				Продолжить;
			КонецЕсли;
			
			Если ЕстьУточнениеПустойСсылки Тогда
				Свойства.ТипыСохраненияПустойСсылки.Добавить(Тип);
			КонецЕсли;
			
			Свойства.ТипыСохраненияЗначений.Добавить(Тип);
		КонецЦикла;
		
		Возврат;
	КонецЕсли;
	
	// Остались только узлы Поле для получения значения Булево.
	Свойства.ТипыСохраненияЗначений.Добавить(Тип("Булево"));
	
	Если Свойства.ТипКонечногоПоля.Типы().Количество() = 1
	   И Свойства.ТипКонечногоПоля.СодержитТип(Тип("Булево")) Тогда
		Возврат;
	КонецЕсли;
	Свойства.ТипКонечногоПоля = Новый ОписаниеТипов("Булево");
	Свойства.ИмяПоляДляЗапроса = Свойства.ИмяПоляДляЗапроса + " = ИСТИНА"; // @query-part-1
	
КонецПроцедуры

// Для функций СвойстваПоля, ДобавитьСвойстваТиповПоля.
Функция ИмяТипаНаЯзыкеЗапросов(Тип, ОбъектМетаданных = Неопределено)
	
	ОбъектМетаданных = Метаданные.НайтиПоТипу(Тип);
	Если ОбъектМетаданных <> Неопределено Тогда
		Возврат ОбъектМетаданных.ПолноеИмя();
	КонецЕсли;
	
	Возврат Строка(Тип);
	
КонецФункции

// Для процедуры ДобавитьСвойстваТиповПоля.
Процедура ДобавитьВидОграниченияПрав(ВидОграничения, СвойстваПоля, Контекст, ПоОбъекту = Ложь)
	
	Если СвойстваПоля.Чтение Тогда
		Контекст.ВсеВидыОграниченийПрав.Вставить("Чтение." + ВидОграничения, Истина);
	КонецЕсли;
	Если Не ПоОбъекту Или СвойстваПоля.Изменение Тогда
		Контекст.ВсеВидыОграниченийПрав.Вставить("Изменение." + ВидОграничения, Истина);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьСвойстваПолей.
//
// Параметры:
//  УзелПоле - см. ОписаниеУзла
//  Контекст - см. КонтекстПараметровПоСтруктуреОграничения
//  СвойстваПоля - см. СвойстваПоля
//
// Возвращаемое значение:
//   Структура:
//     * ПсевдонимТаблицы  - Строка
//     * ИмяПоляДляЗапроса - Строка
//     * ТабличнаяЧасть    - Строка
//     * ЭтоПолеСписка     - Булево
//     * БезЗначенияNull   - Булево
//     * ПолеСодержитNull  - Неопределено
//     * ЕстьВыразить      - Неопределено
//     * УзелПоле          - см. ОписаниеУзла
//     * СвойстваВложения  - см. СвойстваПоля
//     * Чтение            - Булево
//     * ПроверкаЕстьNull  - Неопределено
//     * ТипыСохраненияКлючейДоступа       - Массив из Тип
//     * ТипыСохраненияГруппЗначений       - Массив из Тип
//     * ТипыСохраненияЗначений            - Массив из Тип
//     * ТипыСохраненияПустойСсылки        - Массив из Тип
//     * ТипыСохраненияТипов               - Массив из Тип
//     * ТипыСохраненияТипаЗапрещенный     - Массив из Тип
//     * ТипыСохраненияТипаРазрешенный     - Массив из Тип
//     * НеиспользуемыеТипыЗначенийДоступа - Массив из Тип
//     * ИспользуемыеТипыЗначенийДоступа   - Массив из Тип
//     * НесколькоГруппЗначений                    - Булево
//     * ЭтоСписокЗначенийДоступаСГруппамиЗначений - Булево
//     * ЕстьУточнениеNull                         - Булево
//     * ЕстьУточнениеНеопределено                 - Булево
//     * ЕстьТипВедущегоСписка                     - Булево
//     * ЕстьТипВладельцаНастроекПрав              - Булево
//     * ЕстьПроверкаАвторизованногоПользователя   - Булево
//     * ТипыСохраненияТиповКонфигурации     - Массив из Тип
//     * ТипыСохраненияТиповРасширений       - Массив из Тип
//     * ТипыСохраненияТиповПростых          - Массив из Тип
//     * ИмяГруппыПолейКлючаДоступа          - Строка
//     * ИмяРеквизитаГруппыПолейКлючаДоступа - Строка
//
Функция СвойстваПоля(УзелПоле, Контекст, СвойстваПоля = Неопределено)
	
	Свойства = Новый Структура;
	ЭтоКорневойУзел = СвойстваПоля = Неопределено;
	
	Если ЭтоКорневойУзел Тогда
		СвойстваПоля = Свойства;
		// Внутри функции ЕстьNull() может быть только поле, т.е. функции Выразить() не может быть.
		Если ЗначениеЗаполнено(УзелПоле.Выразить) Тогда
			Типы = Новый Массив;
			Типы.Добавить(ТипСсылкиПоПолномуИмениМетаданных(УзелПоле.Выразить));
			ТипКонечногоПоля = Новый ОписаниеТипов(Типы);
		Иначе
			ТипКонечногоПоля = УзелПоле.ТипыПоля[УзелПоле.ТипыПоля.Количество() - 1];
			Если ЗначениеЗаполнено(УзелПоле.ЕстьNull) Тогда
				Типы = Новый Массив;
				Типы.Добавить(ТипЗначенияУзлаЗначениеИлиКонстанта(УзелПоле.ЕстьNull));
				ТипКонечногоПоля = Новый ОписаниеТипов(ТипКонечногоПоля, Типы);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если УзелПоле.Вложение <> Неопределено Тогда
		СвойстваВложения = СвойстваПоля(УзелПоле.Вложение, Контекст, СвойстваПоля);
		ТабличнаяЧасть    = СвойстваВложения.ТабличнаяЧасть;
		ПсевдонимТаблицы  = СвойстваВложения.ПсевдонимТаблицы;
		ИмяПоляДляЗапроса = СвойстваВложения.ИмяПоляДляЗапроса + "." + УзелПоле.Имя;
		ТипыСтрокой       = СвойстваВложения.ТипыСтрокой + Символы.ПС + УзелПоле.ТипыСтрокой;
	Иначе
		Если ТипЗнч(УзелПоле.ТипыПоля[0]) = Тип("ОписаниеТипов") Тогда
			ТабличнаяЧасть = "";
			Если ЗначениеЗаполнено(УзелПоле.Псевдоним)
			   И УзелПоле.Псевдоним <> Контекст.СтруктураОграничения.ПсевдонимОсновнойТаблицы Тогда
				
				ПсевдонимТаблицы = УзелПоле.Псевдоним;
				ИмяПоляДляЗапроса = ПсевдонимТаблицы + "." + УзелПоле.Имя;
			Иначе
				ПсевдонимТаблицы = "ТекущийСписок";
				ДобавитьОпорноеПоле(Контекст, УзелПоле, СвойстваПоля);
				Если Не ЗначениеЗаполнено(УзелПоле.Выразить) Тогда
					ИмяПоляДляЗапроса = ТекстСОтступом(
						ИмяПоляСРазверткойОпорногоПоляПоТипам(ПсевдонимТаблицы, УзелПоле), "		");
				Иначе
					ИмяПоляДляЗапроса = ПсевдонимТаблицы + "." + УзелПоле.Имя;
				КонецЕсли;
			КонецЕсли;
		Иначе // Первая часть имени поля - это имя табличной части.
			ТабличнаяЧасть = УзелПоле.ТипыПоля[0];
			ПсевдонимТаблицы = "ТекущийСписок" + ТабличнаяЧасть;
			ЧастиИмени = СтрРазделить(УзелПоле.Имя, ".");
			ЧастиИмени.Удалить(0);
			ИмяПоляДляЗапроса = ПсевдонимТаблицы + "." + СтрСоединить(ЧастиИмени, ".");
		КонецЕсли;
		ДобавитьПолеТаблицыОбъекта(Контекст, УзелПоле, СвойстваПоля,, ТабличнаяЧасть);
		ТипыСтрокой = УзелПоле.ТипыСтрокой;
	КонецЕсли;
	
	// Внутри функции ЕстьNull() может быть только поле, т.е. функции Выразить() не может быть.
	Если ЗначениеЗаполнено(УзелПоле.ЕстьNull) Тогда
		ИмяПоляДляЗапроса = "ЕСТЬNULL(" + ИмяПоляДляЗапроса + ", " // @query-part-1
			+ ВыражениеУзлаЗначениеИлиКонстанта(УзелПоле.ЕстьNull) + ")";
		
		Если ЭтоКорневойУзел Тогда
			Свойства.Вставить("БезЗначенияNull", Истина);
		КонецЕсли;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(УзелПоле.Выразить) Тогда
		ИмяПоляДляЗапроса = "ВЫРАЗИТЬ(" + ИмяПоляДляЗапроса + " КАК " + УзелПоле.Выразить + ")"; // @query-part-1 @query-part-2
		СвойстваПоля.Вставить("ЕстьВыразить");
	КонецЕсли;
	
	Свойства.Вставить("ПсевдонимТаблицы",  ПсевдонимТаблицы);
	Свойства.Вставить("ИмяПоляДляЗапроса", ИмяПоляДляЗапроса);
	Свойства.Вставить("ТипКонечногоПоля",  ТипКонечногоПоля);
	Свойства.Вставить("ТипыСтрокой",       ТипыСтрокой);
	
	Если УзелПоле.Свойство("ПолеСодержитNull") Тогда
		Свойства.Вставить("ПолеСодержитNull");
	КонецЕсли;
	
	// Для расчета ведущих списков.
	Свойства.Вставить("ТабличнаяЧасть",    ТабличнаяЧасть);
	Свойства.Вставить("СвойстваВложения",  СвойстваВложения);
	Свойства.Вставить("УзелПоле",          УзелПоле);
	Свойства.Вставить("ЭтоПолеСписка",
		ПсевдонимТаблицы = "ТекущийСписок"
		Или СвойстваВложения <> Неопределено
		  И ЗначениеЗаполнено(СвойстваВложения.ТабличнаяЧасть));
	
	Возврат Свойства;
	
КонецФункции


// Для функции ПараметрыОграниченияПоСтруктуреОграничения.
//
// Возвращаемое значение:
//   Структура:
//     * Поля              - Соответствие
//     * СоединенияОтборов - Соответствие
//
Функция ОписаниеВедущихСписковПоЗначениямПолей()
	
	Возврат Новый Структура("Поля, СоединенияОтборов", Новый Соответствие, Новый Соответствие);
	
КонецФункции

// Для функций ГруппыДополнительныхТаблиц.
Процедура ДобавитьВедущиеСпискиПоЗначениямПолей(Контекст, УзелПоле, СвойстваВложения = Неопределено,
			ГруппаТаблиц = Неопределено, ПсевдонимТаблицыУсловия = Неопределено)
	
	ДополнительныйКонтекст = Новый Структура;
	ДополнительныйКонтекст.Вставить("СвойстваПоляРодителя",    СвойстваВложения);
	ДополнительныйКонтекст.Вставить("ГруппаТаблиц",            ГруппаТаблиц);
	ДополнительныйКонтекст.Вставить("ПсевдонимТаблицыУсловия", ПсевдонимТаблицыУсловия);
	ДополнительныйКонтекст.Вставить("ПсевдонимТаблицыПоля",    УзелПоле.Псевдоним);
	
	ИменаПолей = СтрРазделить(УзелПоле.Имя, ".");
	
	Если УзелПоле.Вложение <> Неопределено Тогда
		ДобавитьПолеВедущегоСпискаПоЗначениямПолей(Контекст, УзелПоле.Таблица, ИменаПолей[0],
			УзелПоле.ТипыПоля[0], ДополнительныйКонтекст, Истина, Истина);
		
	ИначеЕсли ЗначениеЗаполнено(УзелПоле.Псевдоним)
	        И УзелПоле.Псевдоним <> Контекст.СтруктураОграничения.ПсевдонимОсновнойТаблицы Тогда
		
		ДобавитьПолеВедущегоСпискаПоЗначениямПолей(Контекст, УзелПоле.Таблица, ИменаПолей[0],
			УзелПоле.ТипыПоля[0], ДополнительныйКонтекст);
	КонецЕсли;
	
	ИндексПервогоПоля = ?(ТипЗнч(УзелПоле.ТипыПоля[0]) = Тип("ОписаниеТипов"), 0, 1);
	
	Если ИндексПервогоПоля + 2 > ИменаПолей.Количество() Тогда
		Возврат;
	КонецЕсли;
	
	СвойстваПоляРодителя = Новый Структура("ИмяПоляДляЗапроса, ТабличнаяЧасть");
	Если СвойстваВложения <> Неопределено Тогда
		ЗаполнитьЗначенияСвойств(СвойстваПоляРодителя, СвойстваВложения);
		СвойстваПоляРодителя.ИмяПоляДляЗапроса = СвойстваПоляРодителя.ИмяПоляДляЗапроса + "." + ИменаПолей[ИндексПервогоПоля];
	Иначе
		Если ИндексПервогоПоля > 0 Тогда
			СвойстваПоляРодителя.ТабличнаяЧасть = ИменаПолей[0];
		КонецЕсли;
		Если Не ЗначениеЗаполнено(УзелПоле.Псевдоним)
		 Или УзелПоле.Псевдоним = Контекст.СтруктураОграничения.ПсевдонимОсновнойТаблицы Тогда
			
			ПсевдонимПоляРодителя = "ТекущийСписок";
			ДополнительныйКонтекст.ГруппаТаблиц = Неопределено;
		Иначе
			ПсевдонимПоляРодителя = УзелПоле.Псевдоним;
		КонецЕсли;
		СвойстваПоляРодителя.ИмяПоляДляЗапроса = ПсевдонимПоляРодителя + "." + ИменаПолей[ИндексПервогоПоля];
	КонецЕсли;
	ДополнительныйКонтекст.СвойстваПоляРодителя = СвойстваПоляРодителя;
	
	Для Индекс = ИндексПервогоПоля + 1 По ИменаПолей.Количество()-1 Цикл
		Поле = ИменаПолей[Индекс];
		ТипПоля = УзелПоле.ТипыПоля[Индекс];
		Для Каждого Таблица Из УзелПоле.ТаблицыСледующегоПоля[Индекс-1] Цикл
			ДобавитьПолеВедущегоСпискаПоЗначениямПолей(Контекст, Таблица, Поле,
				ТипПоля, ДополнительныйКонтекст, Истина, Истина);
		КонецЦикла;
		СвойстваПоляРодителя.ИмяПоляДляЗапроса = СвойстваПоляРодителя.ИмяПоляДляЗапроса + "." + Поле;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьВедущиеСпискиПоЗначениямПолей.
Процедура ДобавитьПолеВедущегоСпискаПоЗначениямПолей(Контекст, Таблица, Поле, ТипПоля, ДополнительныйКонтекст,
			ЭтоОсновнаяТаблица = Ложь, ЭтоСсылочныйТипТаблицы = Неопределено)
	
	Если Не ЭтоОсновнаяТаблица
	   И СтрЧислоВхождений(Таблица, ".") = 2 Тогда
		
		СоставИмени = СтрРазделить(Таблица, ".");
		ТабличнаяЧасть = СоставИмени[2];
		СоставИмени.Удалить(2);
		ПолноеИмя = СтрСоединить(СоставИмени, ".");
	Иначе
		ТабличнаяЧасть = "";
		ПолноеИмя = Таблица;
	КонецЕсли;
	
	Если ТипЗнч(ДополнительныйКонтекст.ГруппаТаблиц) <> Тип("Массив")
	   И (    ВРег(Поле) = ВРег("Ссылка") // @Non-NLS
	      Или ВРег(Поле) = ВРег("Ref")) Тогда
		
		Если ЭтоСсылочныйТипТаблицы = Неопределено Тогда
			СоставИмени = СтрРазделить(Таблица, ".");
			ТипТаблицы = Контекст.СтруктураОграничения.ВнутренниеДанные.ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
			ЭтоСсылочныйТипТаблицы = ТипТаблицы.ЭтоСсылочныйТип;
		КонецЕсли;
		Если ЭтоСсылочныйТипТаблицы Тогда
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	ОписаниеПолей      = Контекст.ВедущиеСпискиПоЗначениямПолей.Поля.Получить(ПолноеИмя);
	ОписаниеСоединений = Контекст.ВедущиеСпискиПоЗначениямПолей.СоединенияОтборов.Получить(ПолноеИмя);
	
	Если ОписаниеПолей = Неопределено Тогда
		Если ЭтоСсылочныйТипТаблицы = Неопределено Тогда
			СоставИмени = СтрРазделить(Таблица, ".");
			ТипТаблицы = Контекст.СтруктураОграничения.ВнутренниеДанные.ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
			ЭтоСсылочныйТипТаблицы = ТипТаблицы.ЭтоСсылочныйТип;
		КонецЕсли;
		ОписаниеПолей = Новый Структура;
		ОписаниеПолей.Вставить("ЭтоСсылочныйТип", ЭтоСсылочныйТипТаблицы);
		ОписаниеПолей.Вставить("ДляОтслеживания", Новый Структура("ПоляШапки, ТабличныеЧасти",
			Новый Соответствие, Новый Соответствие));
		ОписаниеПолей.Вставить("ДляОтбора", Новый Структура("ПоляШапки, ТабличныеЧасти",
			Новый Соответствие, Новый Соответствие));
		Контекст.ВедущиеСпискиПоЗначениямПолей.Поля.Вставить(ПолноеИмя, ОписаниеПолей);
		
		ОписаниеСоединений = Новый Структура("ПоляШапки, ТабличныеЧасти", Новый Соответствие, Новый Соответствие);
		Контекст.ВедущиеСпискиПоЗначениямПолей.СоединенияОтборов.Вставить(ПолноеИмя, ОписаниеСоединений);
	КонецЕсли;
	
	ВставитьПолеВедущегоСпискаПоЗначениямПолей(ОписаниеПолей.ДляОтслеживания, ТабличнаяЧасть, Поле, ТипПоля);
	
	Если ЗначениеЗаполнено(ТабличнаяЧасть) Тогда
		СоединенияОтборов = ОписаниеСоединений.ТабличныеЧасти.Получить(ТабличнаяЧасть);
		Если СоединенияОтборов = Неопределено Тогда
			СоединенияОтборов = Новый Соответствие;
			ОписаниеСоединений.ТабличныеЧасти.Вставить(ТабличнаяЧасть, СоединенияОтборов);
		КонецЕсли;
	Иначе
		СоединенияОтборов = ОписаниеСоединений.ПоляШапки;
	КонецЕсли;
	
	Если ДополнительныйКонтекст.СвойстваПоляРодителя <> Неопределено Тогда
		Если СоставИмени = Неопределено Тогда
			СоставИмени = СтрРазделить(Таблица, ".");
			ТипТаблицы = Контекст.СтруктураОграничения.ВнутренниеДанные.ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
		КонецЕсли;
		ТипСсылки = Новый ОписаниеТипов(ТипТаблицы.ЯзыкРусский + "Ссылка." + СоставИмени[1]); // @Non-NLS
		ВставитьПолеВедущегоСпискаПоЗначениямПолей(ОписаниеПолей.ДляОтслеживания, "", "Ссылка", ТипСсылки);
		ВставитьПолеВедущегоСпискаПоЗначениямПолей(ОписаниеПолей.ДляОтбора,       "", "Ссылка", ТипСсылки);
		ИмяПоляДляЗапроса = ДополнительныйКонтекст.СвойстваПоляРодителя.ИмяПоляДляЗапроса;
		СоединениеОтбора = СоединенияОтборов.Получить(ВРег(ИмяПоляДляЗапроса));
		Если СоединениеОтбора = Неопределено Тогда
			СоединениеОтбора = Новый Структура;
			СоединениеОтбора.Вставить("ПолеИлиПсевдоним", ИмяПоляДляЗапроса);
			СоединениеОтбора.Вставить("ПоляДляОтбора",    "Ссылка");
			СоединениеОтбора.Вставить("ТабличнаяЧасть",   ДополнительныйКонтекст.СвойстваПоляРодителя.ТабличнаяЧасть);
			СоединениеОтбора.Вставить("ГруппаТаблиц",     ДополнительныйКонтекст.ГруппаТаблиц);
			СоединенияОтборов.Вставить(ВРег(ИмяПоляДляЗапроса), СоединениеОтбора);
		КонецЕсли;
	Иначе
		Если ТипЗнч(ДополнительныйКонтекст.ГруппаТаблиц) = Тип("Массив") Тогда
			СоединениеОтбора = СоединенияОтборов.Получить(ВРег(ДополнительныйКонтекст.ПсевдонимТаблицыПоля));
			Если СоединениеОтбора = Неопределено Тогда
				СоединениеОтбора = Новый Структура;
				СоединениеОтбора.Вставить("ПолеИлиПсевдоним", ДополнительныйКонтекст.ПсевдонимТаблицыПоля);
				СоединениеОтбора.Вставить("ПоляДляОтбора",    Новый Массив);
				СоединениеОтбора.Вставить("ТабличнаяЧасть",   Неопределено);
				СоединениеОтбора.Вставить("ГруппаТаблиц",     ДополнительныйКонтекст.ГруппаТаблиц);
				СоединенияОтборов.Вставить(ВРег(ДополнительныйКонтекст.ПсевдонимТаблицыПоля), СоединениеОтбора);
			КонецЕсли;
			Если СоединениеОтбора.ПоляДляОтбора.Найти(Поле) = Неопределено
			   И ДополнительныйКонтекст.ПсевдонимТаблицыПоля = ДополнительныйКонтекст.ПсевдонимТаблицыУсловия Тогда
				
				СоединениеОтбора.ПоляДляОтбора.Добавить(Поле);
				ВставитьПолеВедущегоСпискаПоЗначениямПолей(ОписаниеПолей.ДляОтбора, ТабличнаяЧасть, Поле, ТипПоля);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуру ДобавитьПолеВедущегоСпискаПоЗначениямПолей.
Процедура ВставитьПолеВедущегоСпискаПоЗначениямПолей(ОписаниеПолей, ТабличнаяЧасть, Поле, ТипПоля)
	
	Если ЗначениеЗаполнено(ТабличнаяЧасть) Тогда
		Поля = ОписаниеПолей.ТабличныеЧасти.Получить(ТабличнаяЧасть);
		Если Поля = Неопределено Тогда
			Поля = Новый Соответствие;
			ОписаниеПолей.ТабличныеЧасти.Вставить(ТабличнаяЧасть, Поля);
		КонецЕсли;
	Иначе
		Поля = ОписаниеПолей.ПоляШапки;
	КонецЕсли;
	
	Если Поля.Получить(Поле) <> Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Поля.Вставить(Поле, Новый ХранилищеЗначения(ТипПоля));
	
КонецПроцедуры

// Для функции ПараметрыОграниченияПоСтруктуреОграничения.
//
// Возвращаемое значение:
//   Структура:
//     * Списки               - Соответствие
//     * КлючиЗапросовПоТипам - Соответствие
//     * СоединенияОтборов    - Соответствие
//     * ТипСсылки            - ОписаниеТипов
//
Функция ОписаниеВедущихСписковПоПолюСсылка()
	
	Описание = Новый Структура;
	Описание.Вставить("Списки",               Новый Соответствие);
	Описание.Вставить("КлючиЗапросовПоТипам", Новый Соответствие);
	Описание.Вставить("СоединенияОтборов",    Новый Соответствие);
	Описание.Вставить("ТипСсылки",            Новый ОписаниеТипов);
	
	Возврат Описание;
	
КонецФункции

// Для процедуры ДобавитьСвойстваТиповПоля.
Процедура ДобавитьВедущийСписокПоПолюСсылка(ВедущиеСписки, ВедущийСписок, УзелПоле, СвойстваПоля, Контекст)
	
	СоставИмени = СтрРазделить(ВедущийСписок, ".");
	ТипТаблицы = Контекст.СтруктураОграничения.ВнутренниеДанные.ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
	ИмяТипаСсылки = ТипТаблицы.ЯзыкРусский + "Ссылка." + СоставИмени[1]; // @Non-NLS
	
	ВедущиеСписки.ТипСсылки = Новый ОписаниеТипов(ВедущиеСписки.ТипСсылки, ИмяТипаСсылки);
	ТипСсылки = Тип(ИмяТипаСсылки);
	
	Ключи = ВедущиеСписки.КлючиЗапросовПоТипам.Получить(ТипСсылки);
	Если Ключи = Неопределено Тогда
		Ключи = Новый Массив;
		ВедущиеСписки.КлючиЗапросовПоТипам.Вставить(ТипСсылки, Ключи);
		ВедущиеСписки.Списки.Вставить(ВедущийСписок, Истина);
	КонецЕсли;
	
	Ключ = ВРег(СвойстваПоля.ИмяПоляДляЗапроса);
	Если Ключи.Найти(Ключ) = Неопределено Тогда
		Ключи.Добавить(Ключ);
	КонецЕсли;
	
	ГруппаТаблиц = ?(СвойстваПоля.ЭтоПолеСписка, Неопределено, СвойстваПоля.ПсевдонимТаблицы);
	
	СоединениеОтбора = ВедущиеСписки.СоединенияОтборов.Получить(Ключ);
	Если СоединениеОтбора = Неопределено Тогда
		СоединениеОтбора = Новый Структура;
		СоединениеОтбора.Вставить("ПолеИлиПсевдоним", СвойстваПоля.ИмяПоляДляЗапроса);
		СоединениеОтбора.Вставить("ПоляДляОтбора",    "Ссылка"); // @query-part-2
		СоединениеОтбора.Вставить("ТабличнаяЧасть",   СвойстваПоля.ТабличнаяЧасть);
		СоединениеОтбора.Вставить("ГруппаТаблиц",     ГруппаТаблиц);
		ВедущиеСписки.СоединенияОтборов.Вставить(Ключ, СоединениеОтбора);
	КонецЕсли;
	
КонецПроцедуры

// Для функции ГруппыДополнительныхТаблиц.
Процедура ЗаполнитьОтборыПоЗначениямПолейВедущегоСписка(ВедущийСписок, Описание, Группы, Контекст)
	
	СоединенияОтборов = Новый Массив;
	Для Каждого СоединениеОтбора Из Описание.ПоляШапки Цикл
		СоединенияОтборов.Добавить(ТекстСоединенияОтбораПоЗначениямПолей(СоединениеОтбора.Значение, Группы, Контекст));
	КонецЦикла;
	Описание.ПоляШапки = СоединенияОтборов;
	
	ТабличныеЧасти = Новый Соответствие;
	Для Каждого ОписаниеТабличнойЧасти Из Описание.ТабличныеЧасти Цикл
		СоединенияОтборов = Новый Массив;
		ТабличныеЧасти.Вставить(ОписаниеТабличнойЧасти.Ключ, СоединенияОтборов);
		Для Каждого СоединениеОтбора Из ОписаниеТабличнойЧасти.Значение Цикл
			СоединенияОтборов.Добавить(ТекстСоединенияОтбораПоЗначениямПолей(СоединениеОтбора.Значение, Группы, Контекст));
		КонецЦикла;
	КонецЦикла;
	Описание.ТабличныеЧасти = ТабличныеЧасти;
	
КонецПроцедуры

// Для функции ГруппыДополнительныхТаблиц.
Процедура ЗаполнитьОтборыВедущихСписковПоПолюСсылка(ВедущиеСписки, Группы, Контекст)
	
	СоединенияОтборов = Новый Соответствие;
	Для Каждого СоединениеОтбора Из ВедущиеСписки.СоединенияОтборов Цикл
		СоединенияОтборов.Вставить(СоединениеОтбора.Ключ,
			ТекстСоединенияОтбораПоЗначениямПолей(СоединениеОтбора.Значение, Группы, Контекст));
	КонецЦикла;
	ВедущиеСписки.СоединенияОтборов = СоединенияОтборов;
	
КонецПроцедуры

// Для процедуры ЗаполнитьОтборыПоЗначениямПолейВедущегоСписка.
Функция ТекстСоединенияОтбораПоЗначениямПолей(Условие, Группы, Контекст)
	
	Если Контекст.ЭтоСсылочныйТип Тогда
		ДополнительноеУсловие = "
		|		И (&УточнениеПланаЗапроса)"; // @query-part-1
	Иначе
		// @query-part-1 @query-part-3
		ДополнительноеУсловие = ?(Не ЗначениеЗаполнено(Контекст.ИмяОтдельногоРегистраКлючей), "
		|		И (ТекущийСписок.Регистр = &ИдентификаторРегистра)", "") + "
		|		И (ТекущийСписок.ВариантДоступа = &ВариантДоступа)
		|		И (&УточнениеПланаЗапроса)";
	КонецЕсли;
	
	Если Условие.ГруппаТаблиц = Неопределено Или ЗначениеЗаполнено(Условие.ТабличнаяЧасть) Тогда
		ТекстСоединения = // @query-part-1
		"#ТекущиеДанныеДляОтбора КАК ТекущиеДанныеДляОтбора
		|	ВНУТРЕННЕЕ СОЕДИНЕНИЕ &ТекущийСписок КАК ТекущийСписок
		|	ПО (" + Условие.ПолеИлиПсевдоним + " = ТекущиеДанныеДляОтбора." + Условие.ПоляДляОтбора + ")";
		Если ЗначениеЗаполнено(Условие.ТабличнаяЧасть) Тогда
			ТекстСоединения = СтрЗаменить(ТекстСоединения,
				"&ТекущийСписок", "&ТекущийСписок." + Условие.ТабличнаяЧасть);
			ТекстСоединения = СтрЗаменить(ТекстСоединения,
				"ТекущийСписок" + Условие.ТабличнаяЧасть + ".", "ТекущийСписок.");
		КонецЕсли;
		Возврат ТекстСоединения + ДополнительноеУсловие;
	КонецЕсли;
	
	Если ТипЗнч(Условие.ГруппаТаблиц) = Тип("Строка") Тогда
		ЭтоПолеУсловияСоединения = Ложь;
		ПсевдонимПолейОтбора = Условие.ГруппаТаблиц;
		Группа = Группы.НомераПоПсевдонимам.Получить(ПсевдонимПолейОтбора);
		ГруппаТаблиц = Группы.ТаблицыПоГруппам.Получить(Группа);
	Иначе
		ЭтоПолеУсловияСоединения = Истина;
		ГруппаТаблиц = Условие.ГруппаТаблиц;
		ЗаменяемоеПоле = Условие.ПолеИлиПсевдоним;
		ПсевдонимПолейОтбора = СтрРазделить(ЗаменяемоеПоле, ".")[0];
	КонецЕсли;
	
	Индекс = ГруппаТаблиц.Количество();
	ПерваяДополнительнаяТаблицаНайдена = Ложь;
	ОбратнаяГруппаТаблиц = Новый Массив;
	ТребуемыеПсевдонимы = Новый Соответствие;
	
	Пока Индекс >= 1 Цикл
		Индекс = Индекс - 1;
		ДополнительнаяТаблица = ГруппаТаблиц[Индекс];
		ТекущийПсевдоним = ДополнительнаяТаблица.Псевдоним;
		Если Не ПерваяДополнительнаяТаблицаНайдена И ТекущийПсевдоним <> ПсевдонимПолейОтбора Тогда
			Продолжить;
		КонецЕсли;
		ПерваяДополнительнаяТаблицаНайдена = Истина;
		Если ТекущийПсевдоним <> ПсевдонимПолейОтбора
		   И ТребуемыеПсевдонимы.Получить(ДополнительнаяТаблица.Псевдоним) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		СвойстваТаблицы = Новый Структура("Таблица, Псевдоним, ПоляУсловияСоединения",
			ДополнительнаяТаблица.Таблица, ДополнительнаяТаблица.Псевдоним, Новый Массив);
		ОбратнаяГруппаТаблиц.Добавить(СвойстваТаблицы);
		Для Каждого ПараПолей Из ДополнительнаяТаблица.ПоляУсловияСоединения Цикл
			ТребуемыеПсевдонимы.Вставить(ПараПолей.ПервоеПоле.Псевдоним, Истина);
			ТребуемыеПсевдонимы.Вставить(ПараПолей.ВтороеПоле.Псевдоним, Истина);
			СвойстваТаблицы.ПоляУсловияСоединения.Добавить(
				Новый Структура(Новый ФиксированнаяСтруктура(ПараПолей)));
		КонецЦикла;
	КонецЦикла;
	
	ВспомогательнаяТаблица = Неопределено;
	ПерваяТаблица = ОбратнаяГруппаТаблиц[0];
	ВсеПервыеПоляЗаменяемые = Истина;
	
	Если ЭтоПолеУсловияСоединения Тогда
		Для Каждого ПараПолей Из ПерваяТаблица.ПоляУсловияСоединения Цикл
			Если СтрНайти(ПараПолей.ПервоеПоле.Поле, ПерваяТаблица.Псевдоним + ".") > 0
			   И СтрНайти(ПараПолей.ПервоеПоле.Поле, ЗаменяемоеПоле + ".") = 0 Тогда
				ВсеПервыеПоляЗаменяемые = Ложь;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Если Не ЭтоПолеУсловияСоединения Или Не ВсеПервыеПоляЗаменяемые Тогда
		ВспомогательнаяТаблица = Новый Структура("Таблица, Псевдоним, ПоляУсловияСоединения",
			ПерваяТаблица.Таблица, "ТекущиеДанныеДляОтбора", Новый Массив);
		ОбратнаяГруппаТаблиц.Вставить(0, ВспомогательнаяТаблица);
		ПараПолей = Новый Структура("ПервоеПоле, ВтороеПоле");
		ВспомогательнаяТаблица.ПоляУсловияСоединения.Добавить(ПараПолей);
		ПараПолей.ПервоеПоле = Новый Структура("Поле, Псевдоним",
			Условие.ПолеИлиПсевдоним, "ТекущиеДанныеДляОтбора");
		ПараПолей.ВтороеПоле = Новый Структура("Поле, Псевдоним",
			"ТекущиеДанныеДляОтбора." + Условие.ПоляДляОтбора, ПсевдонимПолейОтбора);
	КонецЕсли;
	
	ТекстСоединения =
	"#ТекущиеДанныеДляОтбора КАК ТекущиеДанныеДляОтбора";
	ОставшиесяПсевдонимы = Новый Соответствие;
	ОставшиесяПсевдонимы.Вставить("ТекущийСписок", Новый Массив);
	
	Для Каждого ДополнительнаяТаблица Из ОбратнаяГруппаТаблиц Цикл
		ОставшиесяПсевдонимы.Вставить(ДополнительнаяТаблица.Псевдоним,
			Новый Массив(Новый ФиксированныйМассив(ДополнительнаяТаблица.ПоляУсловияСоединения)));
	КонецЦикла;
	
	ПервыеПсевдонимы = Новый Соответствие;
	Индекс = -1;
	Для Каждого ДополнительнаяТаблица Из ОбратнаяГруппаТаблиц Цикл
		Индекс = Индекс + 1;
		
		ПервыйПсевдоним = ДополнительнаяТаблица.Псевдоним;
		ПоляУсловияСоединения = ОставшиесяПсевдонимы.Получить(ПервыйПсевдоним);
		ОставшиесяПсевдонимы.Удалить(ПервыйПсевдоним);
		ПервыеПсевдонимы.Вставить(ПервыйПсевдоним, Истина);
		
		Если Индекс >= ОбратнаяГруппаТаблиц.Количество() - 1 Тогда
			ВторойПсевдоним = "ТекущийСписок";
		Иначе
			ВторойПсевдоним = ОбратнаяГруппаТаблиц[Индекс + 1].Псевдоним;
		КонецЕсли;
		ОстальныеПоляУсловияСоединения = ОставшиесяПсевдонимы.Получить(ВторойПсевдоним);
		Для Каждого ПараПолей Из ОстальныеПоляУсловияСоединения Цикл
			ПоляУсловияСоединения.Добавить(ПараПолей);
		КонецЦикла;
		ОставшиесяПсевдонимы.Вставить(ВторойПсевдоним, Новый Массив);
		
		УсловияСоединения = Новый Массив;
		Для Каждого ПараПолей Из ПоляУсловияСоединения Цикл
			Если Не ЗначениеЗаполнено(ПараПолей.ПервоеПоле.Псевдоним)
			   И Не ЗначениеЗаполнено(ПараПолей.ВтороеПоле.Псевдоним) Тогда
				
				УсловияСоединения.Добавить(ПараПолей.ПервоеПоле.Поле
					+ " = " + ПараПолей.ВтороеПоле.Поле);
				
			ИначеЕсли ПервыеПсевдонимы.Получить(ПараПолей.ПервоеПоле.Псевдоним) <> Неопределено
			        И (Не ЗначениеЗаполнено(ПараПолей.ВтороеПоле.Псевдоним)
			           Или ПараПолей.ВтороеПоле.Псевдоним = ВторойПсевдоним)
			      Или ПараПолей.ПервоеПоле.Псевдоним = ВторойПсевдоним
			        И Не ЗначениеЗаполнено(ПараПолей.ВтороеПоле.Псевдоним) Тогда
				
				ПервоеПоле = ПараПолей.ПервоеПоле.Поле;
				
				Если ПервыеПсевдонимы.Получить(ПараПолей.ПервоеПоле.Псевдоним) <> Неопределено
				   И ПараПолей.ПервоеПоле.Псевдоним = ПсевдонимПолейОтбора
				   И ВспомогательнаяТаблица = Неопределено
				   И СтрНачинаетсяС(ПервоеПоле, ЗаменяемоеПоле) Тогда
					
					КонецПоля = Сред(ПервоеПоле, СтрДлина(ЗаменяемоеПоле) + 1);
					ПервоеПоле = "ТекущиеДанныеДляОтбора"
						+ ?(ЗаменяемоеПоле <> ПсевдонимПолейОтбора, ".Ссылка", "") + КонецПоля; // @query-part-2
				КонецЕсли;
				Если СтрНачинаетсяС(ПервоеПоле, "ТекущиеДанныеДляОтбора.") Тогда
					УсловияСоединения.Добавить(ПараПолей.ВтороеПоле.Поле + " = " + ПервоеПоле);
					
				ИначеЕсли СтрНачинаетсяС(ПараПолей.ВтороеПоле.Поле, "ТекущиеДанныеДляОтбора.") Тогда
					УсловияСоединения.Добавить(ПервоеПоле + " = " + ПараПолей.ВтороеПоле.Поле);
					
				ИначеЕсли ПараПолей.ВтороеПоле.Псевдоним = ВторойПсевдоним Тогда
					УсловияСоединения.Добавить(ПараПолей.ВтороеПоле.Поле + " = " + ПервоеПоле);
				Иначе
					УсловияСоединения.Добавить(ПервоеПоле + " = " + ПараПолей.ВтороеПоле.Поле);
				КонецЕсли;
			Иначе
				ОставшиесяУсловия = ОставшиесяПсевдонимы.Получить(ПараПолей.ПервоеПоле.Псевдоним);
				Если ОставшиесяУсловия = Неопределено Тогда
					ОставшиесяУсловия = ОставшиесяПсевдонимы.Получить(ПараПолей.ВтороеПоле.Псевдоним);
				КонецЕсли;
				ОставшиесяУсловия.Добавить(ПараПолей);
			КонецЕсли;
		КонецЦикла;
		Если ВторойПсевдоним = "ТекущийСписок" Тогда
			ТаблицаИПсевдоним = "&ТекущийСписок КАК ТекущийСписок";
		Иначе
			СледующаяТаблица = ОбратнаяГруппаТаблиц[Индекс + 1];
			ТаблицаИПсевдоним = СледующаяТаблица.Таблица + " КАК " + СледующаяТаблица.Псевдоним;
		КонецЕсли;
		// @query-part-1 @query-part-2 @query-part-3
		ТекстСоединения = ТекстСоединения + "
		|	ВНУТРЕННЕЕ СОЕДИНЕНИЕ " + ТаблицаИПсевдоним + "
		|	ПО (" + СтрСоединить(УсловияСоединения, ")
		|		И (") + ")";
	КонецЦикла;
	
	Возврат ТекстСоединения + ДополнительноеУсловие;
	
КонецФункции


// Для процедур ДобавитьОпорноеПоле, ДобавитьТипыИзмерения, ДобавитьСвойстваТиповПоля,
// ДобавитьПроверкуПоТипам и функции СвойстваПоля.
//
Функция ЭтоПростойТип(Тип)
	
	Возврат Тип = Тип("Булево")
	    Или Тип = Тип("Дата")
	    Или Тип = Тип("Строка")
	    Или Тип = Тип("Число")
	    Или Тип = Тип("УникальныйИдентификатор")
	    Или Тип = Тип("ХранилищеЗначения");
	
КонецФункции

// Для функции ДобавитьСвойстваТиповПоля.
Функция ТипПроверяется(Узел, ПолноеИмя)
	
	Если Узел.Типы.Количество() = 0 Тогда
		Возврат Истина;
	КонецЕсли;
	
	ТипУказан = Ложь;
	Для Каждого ИмяТаблицы Из Узел.Типы Цикл
		Если ВРег(ИмяТаблицы) = ВРег(ПолноеИмя) Тогда
			ТипУказан = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Узел.ПроверятьТипыКромеУказанных И Не ТипУказан
	 Или Не Узел.ПроверятьТипыКромеУказанных И    ТипУказан;
	
КонецФункции

// Для функции ДобавитьСвойстваТиповПоля.
Функция ТипУточнен(Узел, ИмяТипаНаЯзыкеЗапросов)
	
	ТипУточнен = Ложь;
	Для Каждого УточнениеСравнения Из Узел.УточненияСравнения Цикл
		Если ВРег(УточнениеСравнения.Ключ) = ВРег(ИмяТипаНаЯзыкеЗапросов) Тогда
			ТипУточнен = Истина;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат ТипУточнен;
	
КонецФункции

// Для функции ДобавитьСвойстваТиповПоля.
Функция УточнениеТипа(Узел, ИмяТипаНаЯзыкеЗапросов)
	
	Уточнение = "";
	Для Каждого УточнениеСравнения Из Узел.УточненияСравнения Цикл
		Если ВРег(УточнениеСравнения.Ключ) = ВРег(ИмяТипаНаЯзыкеЗапросов) Тогда
			Уточнение = УточнениеСравнения.Значение;
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Уточнение;
	
КонецФункции

// Для функции СвойстваПоля.
Функция ТипЗначенияУзлаЗначениеИлиКонстанта(Узел)
	
	Если Узел.Узел = "Значение" Тогда
		Возврат ТипСсылкиПоПолномуИмениПредопределенного(Узел.Имя);
	КонецЕсли;
	
	Возврат ТипЗнч(Узел.Значение);
	
КонецФункции

// Для функций ТекстУсловияСоединения, СвойстваПоля, НаборПолейУсловияКогда и
// процедуры УточнитьСвойстваПоляСравнения.
//
Функция ВыражениеУзлаЗначениеИлиКонстанта(Узел)
	
	Если Узел.Узел = "Значение" Тогда
		Выражение = "Значение(" + Узел.Имя + ")";
	
	ИначеЕсли ТипЗнч(Узел.Значение) = Тип("Булево") Тогда
		Выражение = ?(Узел.Значение, "Истина", "Ложь");
		
	ИначеЕсли ТипЗнч(Узел.Значение) = Тип("Число") Тогда
		Выражение = Формат(Узел.Значение, "ЧГ=");
		
	ИначеЕсли ТипЗнч(Узел.Значение) = Тип("Неопределено") Тогда
		Выражение = "Неопределено";
	Иначе
		Выражение = """" + Узел.Значение + """";
	КонецЕсли;
	
	Возврат Выражение;
	
КонецФункции

// Для функции ТипЗначенияУзлаЗначениеИлиКонстанта.
Функция ТипСсылкиПоПолномуИмениПредопределенного(ПолноеИмяПредопределенного)
	
	ЧастиИмени = СтрРазделить(ПолноеИмяПредопределенного, ".");
	ЧастиИмени.Удалить(2);
	
	ПолноеИмя = СтрСоединить(ЧастиИмени, ".");
	
	Возврат ТипСсылкиПоПолномуИмениМетаданных(ПолноеИмя);
	
КонецФункции

// Для функций СвойстваПоля, ТипСсылкиПоПолномуИмениПредопределенного.
Функция ТипСсылкиПоПолномуИмениМетаданных(ПолноеИмя)
	
	Если ЭтоРусскийВариантВидаОбъектаМетаданных(ПолноеИмя) Тогда
		ИмяТипаСсылки = СтрЗаменить(ПолноеИмя, ".", "Ссылка."); // @Non-NLS-2
	Иначе
		ИмяТипаСсылки = СтрЗаменить(ПолноеИмя, ".", "Ref.");
	КонецЕсли;
	
	Возврат Тип(ИмяТипаСсылки);
	
КонецФункции

// Для функции КлючТаблицы.
Функция ТипКлючаЗаписиПоПолномуИмениМетаданных(ПолноеИмя)
	
	Если ЭтоРусскийВариантВидаОбъектаМетаданных(ПолноеИмя) Тогда
		ИмяТипаКлючаЗаписи = СтрЗаменить(ПолноеИмя, ".", "КлючЗаписи."); // @Non-NLS-2
	Иначе
		ИмяТипаКлючаЗаписи = СтрЗаменить(ПолноеИмя, ".", "RecordKey.");
	КонецЕсли;
	
	Возврат Тип(ИмяТипаКлючаЗаписи);
	
КонецФункции

// Для функции КлючТаблицы.
Функция ТипНабораЗаписейПоПолномуИмениМетаданных(ПолноеИмя)
	
	Если ЭтоРусскийВариантВидаОбъектаМетаданных(ПолноеИмя) Тогда
		ИмяТипаНабораЗаписей = СтрЗаменить(ПолноеИмя, ".", "НаборЗаписей."); // @Non-NLS-2
	Иначе
		ИмяТипаНабораЗаписей = СтрЗаменить(ПолноеИмя, ".", "RecordSet.");
	КонецЕсли;
	
	Возврат Тип(ИмяТипаНабораЗаписей);
	
КонецФункции

// Для функции КлючТаблицы.
Функция ТипМенеджераОбъектаПоПолномуИмениМетаданных(ПолноеИмя)
	
	Если ЭтоРусскийВариантВидаОбъектаМетаданных(ПолноеИмя) Тогда
		ИмяТипаМенеджера = СтрЗаменить(ПолноеИмя, ".", "Менеджер."); // @Non-NLS-2
	Иначе
		ИмяТипаМенеджера = СтрЗаменить(ПолноеИмя, ".", "Manager.");
	КонецЕсли;
	
	Возврат Тип(ИмяТипаМенеджера);
	
КонецФункции

// Для функций ТипСсылкиПоПолномуИмениМетаданных, ТипКлючаЗаписиПоПолномуИмениМетаданных,
// ТипНабораЗаписейПоПолномуИмениМетаданных, ТипМенеджераОбъектаПоПолномуИмениМетаданных.
//
Функция ЭтоРусскийВариантВидаОбъектаМетаданных(ПолноеИмя)
	
	ПервыйСимвол = Лев(ПолноеИмя, 1);
	
	Возврат ПервыйСимвол > "А"  // @Non-NLS
	      И ПервыйСимвол < "Я"  // @Non-NLS
	    Или ПервыйСимвол > "а"  // @Non-NLS
	      И ПервыйСимвол < "я"; // @Non-NLS
	
КонецФункции

#КонецОбласти

#Область ПараметрыОграниченияДоступаТекстыЗапросовДляСписка

// Возвращаемое значение:
//  Соответствие из КлючИЗначение:
//   * Ключ - Строка
//   * Значение - Массив из Строка
//
Функция НовыеРеквизитыТаблицКлюча()
	Возврат Новый Соответствие;
КонецФункции

// Основная функция области, которая является второй частью
// функции ПараметрыОграниченияПоСтруктуреОграничения, но
// не используется при подготовке хранимых параметров ограничения доступа,
// а используется при вызове функции ПараметрыОграничения.
//
Процедура ДобавитьТекстыЗапросовВПараметрыОграничения(Результат)
	
	ПроверитьОпределяемыйТипПолеРегистраКлючейДоступаКРегистрам(Результат.Контекст);
	
	Контекст = Результат.Контекст;
	Результат.Удалить("Контекст");
	
	// Проверка прав Чтение и Изменение объекта или набора записей в базе данных.
	Результат.Вставить("ТекстЗапросаПроверкиПравЧтениеИзменение");
	// Проверка права Чтение объекта или набора записей в базе данных.
	Результат.Вставить("ТекстЗапросаПроверкиПраваЧтение");
	// Текст извлечения владельца из ссылки объекта.
	Результат.Вставить("ПолеОбъектаВладельцаВЗапросеПроверкиПрав");
	
	ДобавитьТекстЗапросаДатыСледующегоЭлементаДанных(Результат, Контекст); 
	
	Если Результат.БезЗаписиКлючейДоступа Тогда
		// Запрос объектов или отборов записей для удаления или установки пустых ключей.
		Результат.Вставить("ТекстЗапросаУстаревшихЭлементовДанных");
		ДобавитьТекстЗапросаУстаревшихЭлементовДанных(Результат, Контекст);
		
		Если Результат.ИспользуетсяОграничениеПоВладельцу Тогда
			// Формирование запросов проверки прав.
			ЗаполнитьЗапросыПроверкиПравЧтениеИзменение(Результат, Контекст);
		КонецЕсли;
		
		Возврат;
	КонецЕсли;
	
	// Имена используемых таблиц ключа.
	Результат.Вставить("ТаблицыКлюча");
	// Имена используемых реквизитов таблиц ключа.
	Результат.Вставить("РеквизитыТаблицКлюча");
	
	// Запрос объектов или отборов записей, у которых устарели ключи доступа.
	Результат.Вставить("ТекстЗапросаЭлементовДанныхСУстаревшимиКлючами");
	// Запрос отборов записей, которых нет в регистре ключей доступа регистров при фоновом обновлении.
	Результат.Вставить("ТекстЗапросаЭлементовДанныхБезКлючейДоступа");
	// Запрос отборов записей, которых нет в регистре ключей доступа регистров при записи нового набора.
	Результат.Вставить("ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейСуществующихЗаписей");
	Результат.Вставить("ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейНовыхЗаписей");
	// Запросы объектов или отборов записей, у которых устарели ключи доступа
	// по составу изменений ведущих объектов (для точечного задания).
	Результат.Вставить("ОписаниеЗапросовУстаревшихКлючейДоступаПоВедущимОбъектам", Новый Соответствие);
	// Запрос несуществующих объектов и отборов записей, которые не используются.
	Результат.Вставить("ТекстЗапросаУстаревшихЭлементовДанных");
	
	// Запрос текущих ключей доступа отборов записей регистра перед записью рассчитанных ключей доступа.
	Результат.Вставить("ТекстЗапросаТекущихКлючейДоступаРегистра");
	
	// Запрос значений объектов или отборов записей.
	Результат.Вставить("ТекстЗапросаЗначенийЭлементовДанныхДляКлючейДоступа");
	// Запрос значений объектов в памяти.
	Результат.Вставить("ТекстЗапросаЗначенийОбъектовВПамятиДляКлючейДоступа");
	// Запрос значений из используемых ключей доступа для сравнения со значениями из объектов или отборов записей.
	Результат.Вставить("ТекстЗапросаЗначенийИзИспользуемыхКлючейДоступаДляСравнения");
	// Запрос значений из всех ключей доступа для сравнения со значениями из объектов или
	// отборов записей перед записью нового ключа.
	Результат.Вставить("ТекстЗапросаЗначенийИзВсехКлючейДоступаДляСравнения");
	// Запрос проверки существования ключа доступа перед записью нового ключа.
	Результат.Вставить("ТекстЗапросаСуществованияКлючейДляСравнения");
	
	// Запрос ключей доступа для обновления пользователей и групп доступа, которым они разрешены.
	Результат.Вставить("ТекстЗапросаКлючейДоступаДляОбновленияПрав");
	// Запрос ключей доступа по ведущим ключам для обновления пользователей и групп доступа, которым они разрешены.
	Результат.Вставить("ТекстЗапросаКлючейПоВедущимКлючамДляОбновленияПрав");
	// Запрос значений из ключей доступа для вычисления пользователей и групп доступа, которым они разрешены.
	Результат.Вставить("ТекстЗапросаЗначенийИзКлючейДоступаДляРасчетаПрав");
	// Запрос неиспользуемых ключей доступа для установки даты неиспользования или удаления.
	Результат.Вставить("ТекстЗапросаУстаревшихКлючейДоступа");
	
	Контекст.Вставить("ОтдельныйРегистр", Истина); // Уточнение регистра ключей для нессылочных типов.
	
	Контекст.Вставить("ТаблицыКлюча",                                       Новый Массив);
	Контекст.Вставить("РеквизитыТаблицКлюча",                               НовыеРеквизитыТаблицКлюча());
	Контекст.Вставить("ЧастиУсловияПроверки",                               Новый Массив);
	Контекст.Вставить("ЧастиЗапросаЗначенийИзОбъектов",                     Новый Массив);
	Контекст.Вставить("ЧастиЗапросаЗначенийИзОбъектовВПамяти",              Новый Массив);
	Контекст.Вставить("ЧастиЗапросаЗначенийИзКлючейДляСравнения",           Новый Массив);
	Контекст.Вставить("ЧастиЗапросаСуществованияКлючейДляСравнения",        Новый Массив);
	Контекст.Вставить("ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав",         Новый Массив);
	Контекст.Вставить("ЧастиУсловияВыбораПравВедущихКлючейДоступа",         Новый Массив);
	Контекст.Вставить("ЧастиУсловияВыбораПравВедущихСписков",               Новый Массив);
	Контекст.Вставить("ЧастиУсловияВыбораПравПоВладельцамНастроекПрав",     Новый Массив);
	Контекст.Вставить("ЧастиУсловияОтбораПоВедущимКлючамДоступа",           Новый Массив);
	Контекст.Вставить("ОписаниеЗапросовПроверкиПоВедущимОбъектам",          Новый Соответствие);
	
	// Формирование запроса элементов данных с устаревшими ключами.
	ЗаполнитьШаблоныЧастейЗапросаПроверки(Контекст);
	
	Для НомерШапки = 0 По 2 Цикл
		ДобавитьПроверкуШапкиКлюча(Контекст, НомерШапки);
	КонецЦикла;
	
	Для НомерТабличнойЧастиКлюча = 1 По Контекст.КоличествоТабличныхЧастейКлюча Цикл
		ДобавитьПроверкуТабличнойЧастиКлюча(Контекст, НомерТабличнойЧастиКлюча);
	КонецЦикла;
	СобратьЧастиЗапросаПроверки(Результат, Контекст);
	
	// Формирование запросов значений из элементов данных и из ключей.
	Для НомерШапки = 0 По 2 Цикл
		ДобавитьЗаполнениеШапкиКлюча(Контекст, НомерШапки);
	КонецЦикла;
	ДобавитьВыборКлючейБезПолейВШапке(Контекст);
	
	Для НомерТабличнойЧастиКлюча = 1 По Контекст.КоличествоТабличныхЧастейКлюча Цикл
		ДобавитьЗаполнениеТабличнойЧастиКлюча(Контекст, НомерТабличнойЧастиКлюча);
	КонецЦикла;
	СобратьЧастиЗапросовЗаполнения(Результат, Контекст);
	
	Результат.ТаблицыКлюча         = Контекст.ТаблицыКлюча;
	Результат.РеквизитыТаблицКлюча = Контекст.РеквизитыТаблицКлюча;
	
	ДобавитьТекстЗапросаУстаревшихЭлементовДанных(Результат, Контекст);
	
	// Формирование запросов проверки прав.
	ЗаполнитьЗапросыПроверкиПравЧтениеИзменение(Результат, Контекст);
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ПроверитьОпределяемыйТипПолеРегистраКлючейДоступаКРегистрам(Контекст)
	
	Если Контекст.ЭтоСсылочныйТип Или Контекст.ИспользуетсяОграничениеПоВладельцу Тогда
		Возврат;
	КонецЕсли;
	
	Если Контекст.Свойство("ПропуститьПроверкуОпределяемыхТипов") Тогда
		Возврат;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Контекст.ИмяОтдельногоРегистраКлючей) Тогда
		Измерения = Метаданные.РегистрыСведений[Контекст.ИмяОтдельногоРегистраКлючей].Измерения;
	Иначе
		Измерения = Метаданные.РегистрыСведений.КлючиДоступаКРегистрам.Измерения;
	КонецЕсли;
	
	НедостающиеТипы = Новый Соответствие;
	Поля = Новый Соответствие;
	СписокНедостающихТипов = Новый СписокЗначений;
	СписокПолей = Новый СписокЗначений;
	
	Индекс = Контекст.ОпорныеПоля.ТипыВсех.Количество() - 1;
	Пока Индекс >= 0 Цикл
		ТребуемыйТип = Контекст.ОпорныеПоля.ТипыВсех.Получить(Индекс).Получить();
		ТипИзмерения = Измерения["Поле" + (Индекс + 1)].Тип;
		НедостающийТип = Новый ОписаниеТипов(ТребуемыйТип, , ТипИзмерения.Типы());
		Для Каждого Тип Из НедостающийТип.Типы() Цикл
			Если НедостающиеТипы.Получить(Тип) = Неопределено Тогда
				НедостающиеТипы.Вставить(Тип, Истина);
				СписокНедостающихТипов.Добавить(ИмяТипаСсылки(ИмяТипаНаЯзыкеЗапросов(Тип),
					Контекст.СтруктураОграничения.ВнутренниеДанные.ТипыТаблицПоИменам));
				Поле = Контекст.ОпорныеПоля.Все.Получить(Индекс);
				Если Поля.Получить(Поле) = Неопределено Тогда
					Поля.Вставить(Поле, Истина);
					СписокПолей.Добавить(Поле);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
		Индекс = Индекс - 1;
	КонецЦикла;
	
	Если СписокНедостающихТипов.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	СписокНедостающихТипов.СортироватьПоЗначению();
	
	Если ЗначениеЗаполнено(Контекст.ИмяОтдельногоРегистраКлючей) Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для списка %1
			           |не удалось подготовить параметры ограничения доступа к работе,
			           |так как в измерениях регистра сведений
			           |	%2
			           |не хватает типов:
			           |	- %3,
			           |имеющихся у полей (%4) в ограничении доступа:
			           |	%5'"),
			Контекст.Список,
			Контекст.ИмяОтдельногоРегистраКлючей,
			СтрСоединить(СписокНедостающихТипов.ВыгрузитьЗначения(), "," + Символы.ПС + "	- "),
			СтрСоединить(СписокПолей.ВыгрузитьЗначения(), ", "),
			ТекстСОтступом(Контекст.ОписанияОграничений.Получить(Контекст.Список).Текст, "	"));
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Для списка %1
			           |не удалось подготовить параметры ограничения доступа к работе, так как
			           |в определяемом типе %2 не хватает типов:
			           |	- %3,
			           |имеющихся у полей (%4) в ограничении доступа:
			           |	%5'"),
			Контекст.Список,
			"ПолеРегистраКлючейДоступаКРегистрам",
			СтрСоединить(СписокНедостающихТипов.ВыгрузитьЗначения(), "," + Символы.ПС + "	- "),
			СтрСоединить(СписокПолей.ВыгрузитьЗначения(), ", "),
			ТекстСОтступом(Контекст.ОписанияОграничений.Получить(Контекст.Список).Текст, "	"));
	КонецЕсли;
	
	ВызватьИсключение ТекстОшибки;
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ДобавитьТекстЗапросаДатыСледующегоЭлементаДанных(Результат, Контекст)
	
	Если Не Результат.СписокСДатой И Не Результат.СписокСПериодом Тогда
		Возврат;
	КонецЕсли;
	
	Если Результат.СписокСДатой Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ТекущийСписок.Дата КАК Дата
		|ИЗ
		|	&ТекущийСписок КАК ТекущийСписок
		|ГДЕ
		|	ТекущийСписок.Дата < &ДатаНачала
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Дата УБЫВ";
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ТекущийСписок.Период КАК Период
		|ИЗ
		|	&ТекущийСписок КАК ТекущийСписок
		|ГДЕ
		|	ТекущийСписок.Период < &ДатаНачала
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Период УБЫВ";
		Если Контекст.ИмяКоллекцииТипа = "РегистрыРасчета" Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ".Период", ".ПериодРегистрации"); // @query-part-1 @query-part-2
		КонецЕсли;
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийСписок", Результат.Список);
	
	Результат.Вставить("ТекстЗапросаДатыСледующегоЭлементаДанных", ТекстЗапроса);
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ДобавитьТекстЗапросаУстаревшихЭлементовДанных(Результат, Контекст)
	
	Если Результат.ЭтоСсылочныйТип Тогда
		ТипСсылки = ?(ЗначениеЗаполнено(Результат.Версия)
				Или ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(Результат.Список) <> Неопределено,
			ТипСсылкиПоПолномуИмениМетаданных(Результат.Список), Неопределено);
		ТипыСсылок = УправлениеДоступомСлужебныйПовтИсп.ТипыПоляТаблицы("ОпределяемыйТип.ВладелецЗначенийКлючейДоступа");
		
		Если ТипыСсылок.Получить(ТипСсылки) = Неопределено Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 0
			|	КлючиДоступаКОбъектам.Объект КАК ТекущаяСсылка
			|ИЗ
			|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам"; // @query-part-1
		ИначеЕсли Результат.ДляВнешнихПользователей Тогда
			Если Результат.БезЗаписиКлючейДоступа
			   И Не Результат.БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей Тогда
				ТекстЗапроса =
				"ВЫБРАТЬ ПЕРВЫЕ 995
				|	КлючиДоступаКОбъектам.Объект КАК ТекущаяСсылка
				|ИЗ
				|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
				|ГДЕ
				|	ТИПЗНАЧЕНИЯ(КлючиДоступаКОбъектам.Объект) = ТИП(&ТекущийСписок)
				|	И ВЫРАЗИТЬ(КлючиДоступаКОбъектам.Объект КАК &ТекущийСписок) > &ПоследняяОбработаннаяСсылка
				|	И КлючиДоступаКОбъектам.КлючДоступаВнешнихПользователей <> ЗНАЧЕНИЕ(Справочник.КлючиДоступа.ПустаяСсылка)
				|	И &УточнениеПланаЗапроса
				|
				|УПОРЯДОЧИТЬ ПО
				|	КлючиДоступаКОбъектам.Объект"; // @query-part-1
			Иначе
				ТекстЗапроса = "";
			КонецЕсли;
		ИначеЕсли Результат.БезЗаписиКлючейДоступаДляПользователейИВнешнихПользователей Тогда
			ТекстЗапроса = 
			"ВЫБРАТЬ ПЕРВЫЕ 995
			|	КлючиДоступаКОбъектам.Объект КАК ТекущаяСсылка
			|ИЗ
			|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
			|ГДЕ
			|	ТИПЗНАЧЕНИЯ(КлючиДоступаКОбъектам.Объект) = ТИП(&ТекущийСписок)
			|	И ВЫРАЗИТЬ(КлючиДоступаКОбъектам.Объект КАК &ТекущийСписок) > &ПоследняяОбработаннаяСсылка
			|	И &УточнениеПланаЗапроса
			|
			|УПОРЯДОЧИТЬ ПО
			|	КлючиДоступаКОбъектам.Объект"; // @query-part-1
		Иначе
			ТекстЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 995
			|	КлючиДоступаКОбъектам.Объект КАК ТекущаяСсылка,
			|	ТекущийСписок.Ссылка ЕСТЬ NULL КАК Удалить
			|ИЗ
			|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
			|		ЛЕВОЕ СОЕДИНЕНИЕ &ТекущийСписок КАК ТекущийСписок
			|		ПО (ТИПЗНАЧЕНИЯ(КлючиДоступаКОбъектам.Объект) = ТИП(&ТекущийСписок))
			|			И (ТекущийСписок.Ссылка = КлючиДоступаКОбъектам.Объект)
			|ГДЕ
			|	ТИПЗНАЧЕНИЯ(КлючиДоступаКОбъектам.Объект) = ТИП(&ТекущийСписок)
			|	И ВЫРАЗИТЬ(КлючиДоступаКОбъектам.Объект КАК &ТекущийСписок) > &ПоследняяОбработаннаяСсылка
			|	И &УсловиеЗапроса
			|	И &УточнениеПланаЗапроса
			|
			|УПОРЯДОЧИТЬ ПО
			|	КлючиДоступаКОбъектам.Объект"; // @query-part-1
			Если Результат.БезЗаписиКлючейДоступа Тогда
				Условие =
				"(КлючиДоступаКОбъектам.КлючДоступаПользователей <> ЗНАЧЕНИЕ(Справочник.КлючиДоступа.ПустаяСсылка)
				|		ИЛИ ТекущийСписок.Ссылка ЕСТЬ NULL)"; // @query-part-1
			Иначе
				Условие = "ТекущийСписок.Ссылка ЕСТЬ NULL"; // @query-part-1
			КонецЕсли;
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеЗапроса", Условие);
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийСписок", Результат.Список);
		Результат.ТекстЗапросаУстаревшихЭлементовДанных = ТекстЗапроса;
		Возврат;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Результат.ИмяОтдельногоРегистраКлючей) Тогда
		ИмяРегистра   = Результат.ИмяОтдельногоРегистраКлючей;
		ОсновнойОтбор = "ИСТИНА"; // @query-part-1
	Иначе
		ИмяРегистра   = "КлючиДоступаКРегистрам";
		ОсновнойОтбор = "КлючиДоступаКРегистрам.Регистр = &ИдентификаторРегистра";
	КонецЕсли;
	
	ОтборПоВидуПользователей = "ВЫБОР
	|			КОГДА КлючиДоступаКРегистрам.ВариантДоступа = 0
	|				ТОГДА ЛОЖЬ
	|			ИНАЧЕ КлючиДоступаКРегистрам.ВариантДоступа - (ВЫРАЗИТЬ(КлючиДоступаКРегистрам.ВариантДоступа / 2 - 0.5 КАК ЧИСЛО(15, 0))) * 2 = 1
	|		КОНЕЦ = " + ?(Результат.ДляВнешнихПользователей, "ИСТИНА", "ЛОЖЬ"); // @query-part-1
	
	ОпорныеПоля = Контекст.ОпорныеПоля;
	Если ОпорныеПоля = Неопределено Тогда
		МаксимальноеКоличествоОпорныхПолей =
			УправлениеДоступомСлужебныйПовтИсп.КоличествоОпорныхПолейРегистра(ИмяРегистра);
	Иначе
		МаксимальноеКоличествоОпорныхПолей = ОпорныеПоля.МаксимальноеКоличество;
	КонецЕсли;
	
	ОпорныеПоляВыбора = "КлючиДоступаКРегистрам.ВариантДоступа КАК ВариантДоступа";
	ОпорныеПоляДляОтбора = "";
	ОпорныеПоляДляУпорядочения = "КлючиДоступаКРегистрам.ВариантДоступа";
	
	Для Номер = 1 По МаксимальноеКоличествоОпорныхПолей Цикл
		// Поля для выбора.
		ОпорныеПоляВыбора = ОпорныеПоляВыбора + ?(ОпорныеПоляВыбора = "", "", ",
		|	") + "КлючиДоступаКРегистрам.Поле" + Номер + " КАК Поле" + Номер; // @query-part-3
		
		// Поля для отбора.
		Отбор = "";
		Для ДополнительныйНомер = 1 По Номер - 1 Цикл
			Отбор = Отбор + ?(ДополнительныйНомер = 1, "", "
			|	И ") + "КлючиДоступаКРегистрам.Поле" + ДополнительныйНомер + " = &Поле" + ДополнительныйНомер; // @query-part-1
		КонецЦикла;
		Отбор = Отбор + ?(Отбор = "", "", "
		|	И ") + "КлючиДоступаКРегистрам.Поле" + Номер + " > &Поле" + Номер; // @query-part-1
		
		Отбор = ?(Номер = 1, ?(МаксимальноеКоличествоОпорныхПолей > 1, "(", "") + Отбор, "
		|			ИЛИ " + ТекстСОтступом(Отбор, "			")); // @query-part-1
		
		ОпорныеПоляДляОтбора = ОпорныеПоляДляОтбора + ?(ОпорныеПоляДляОтбора = "", "", ?(Номер = 1, "
		|	И ", "")) + Отбор; // @query-part-1
		
		// Поля для упорядочения.
		ОпорныеПоляДляУпорядочения = ОпорныеПоляДляУпорядочения + ?(ОпорныеПоляДляУпорядочения = "", "", ",
		|	") + "КлючиДоступаКРегистрам.Поле" + Номер;
	КонецЦикла;
	Если МаксимальноеКоличествоОпорныхПолей > 1 Тогда
		ОпорныеПоляДляОтбора  = ОпорныеПоляДляОтбора  + ")";
	КонецЕсли;
	
	Если ЗначениеЗаполнено(Результат.ИмяОтдельногоРегистраКлючей) Тогда
		ТекстЗапросаОчистки = 
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	&ОпорныеПоляВыбора
		|ИЗ
		|	РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
		|ГДЕ
		|	КлючиДоступаКРегистрам.Регистр = &ИдентификаторРегистра
		|	И &ОтборПоВидуПользователей
		|	И &ОпорныеПоляДляОтбора
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	&ОпорныеПоляДляУпорядочения";
		ТекстЗапросаОчистки = СтрЗаменить(ТекстЗапросаОчистки, "&ОпорныеПоляВыбора", ОпорныеПоляВыбора);
		ТекстЗапросаОчистки = СтрЗаменить(ТекстЗапросаОчистки, "&ОтборПоВидуПользователей", ОтборПоВидуПользователей);
		ТекстЗапросаОчистки = СтрЗаменить(ТекстЗапросаОчистки, "&ОпорныеПоляДляОтбора", ОпорныеПоляДляОтбора);
		ТекстЗапросаОчистки = СтрЗаменить(ТекстЗапросаОчистки, "&ОпорныеПоляДляУпорядочения", ОпорныеПоляДляУпорядочения);
		Результат.Вставить("ТекстЗапросаУстаревшихЭлементовДанныхИзОбщегоРегистра", ТекстЗапросаОчистки);
	КонецЕсли;
	
	Если Результат.БезЗаписиКлючейДоступа Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	&ОпорныеПоляВыбора
		|ИЗ
		|	#ИмяРегистра КАК КлючиДоступаКРегистрам
		|ГДЕ
		|	&ОсновнойОтбор
		|	И &ОтборПоВидуПользователей
		|	И &ОпорныеПоляДляОтбора
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	&ОпорныеПоляДляУпорядочения";
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляВыбора", ОпорныеПоляВыбора);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяРегистра", "РегистрСведений." + ИмяРегистра);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОсновнойОтбор", ОсновнойОтбор);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОтборПоВидуПользователей", ОтборПоВидуПользователей);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляДляОтбора", ОпорныеПоляДляОтбора);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляДляУпорядочения", ОпорныеПоляДляУпорядочения);
	Иначе
		Номер = 1;
		ОпорныеПоляВыбораПриПроверке = "";
		ОпорныеПоляДляСравнения = "";
		Для Каждого ИмяОпорногоПоля Из ОпорныеПоля.Используемые Цикл
			// Поля для выбора при проверке устаревания перед записью.
			ОпорныеПоляВыбораПриПроверке = ОпорныеПоляВыбораПриПроверке + ?(ОпорныеПоляВыбораПриПроверке = "", "", ",
			|	") + "КлючиДоступаКРегистрам.Поле" + Номер + " КАК Поле" + Номер; // @query-part-3
			// Поля для сравнения.
			ОпорныеПоляДляСравнения = ОпорныеПоляДляСравнения + ?(ОпорныеПоляДляСравнения = "", "", "
			|И ") + "ТекущийСписок." + ИмяОпорногоПоля + " = КлючиДоступаКРегистрам.Поле" + Номер; // @query-part-1
			Номер = Номер + 1;
		КонецЦикла;
		
		ТекстЗапроса = 
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	&ОпорныеПоляВыбора,
		|	НЕ КлючиДоступаКРегистрам.ВариантДоступа В (&ИспользуемыеВариантыДоступа) КАК Удалить
		|ИЗ
		|	#ИмяРегистра КАК КлючиДоступаКРегистрам
		|ГДЕ
		|	&ОсновнойОтбор
		|	И &ОтборПоВидуПользователей
		|	И &ОпорныеПоляДляОтбора
		|	И (НЕ КлючиДоступаКРегистрам.ВариантДоступа В (&ИспользуемыеВариантыДоступа)
		|			ИЛИ КлючиДоступаКРегистрам.ВариантДоступа = &ВариантДоступа
		|				И НЕ ИСТИНА В
		|						(ВЫБРАТЬ ПЕРВЫЕ 1
		|							ИСТИНА
		|						ИЗ
		|							&ТекущийСписок КАК ТекущийСписок
		|						ГДЕ
		|							&ОпорныеПоляДляСравнения))
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	&ОпорныеПоляДляУпорядочения";
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляВыбора", ОпорныеПоляВыбора);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяРегистра", "РегистрСведений." + ИмяРегистра);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОсновнойОтбор", ОсновнойОтбор);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОтборПоВидуПользователей", ОтборПоВидуПользователей);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляДляОтбора", ОпорныеПоляДляОтбора);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляДляСравнения", 
			ТекстСОтступом(ОпорныеПоляДляСравнения, "						"));
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляДляУпорядочения", ОпорныеПоляДляУпорядочения);

		ИспользуемыеВариантыДоступа = Контекст.ОсновныеВариантыДоступа.Получить(Контекст.Список);
		ВариантыДоступаСтрокой = Новый Массив;
		Если ИспользуемыеВариантыДоступа = Неопределено Тогда
			ВариантыДоступаСтрокой.Добавить("NULL");
		Иначе
			Для Каждого ИспользуемыйВариантДоступа Из ИспользуемыеВариантыДоступа Цикл
				ВариантыДоступаСтрокой.Добавить(XMLСтрока(ИспользуемыйВариантДоступа.ВариантДоступа));
			КонецЦикла;
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ИспользуемыеВариантыДоступа",
			СтрСоединить(ВариантыДоступаСтрокой, ","));
		
		ВариантДоступаСтрокой = XMLСтрока(Контекст.ВариантДоступа);
		ВариантДоступаСтрокой = ?(ЗначениеЗаполнено(ВариантДоступаСтрокой), ВариантДоступаСтрокой, "NULL");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ВариантДоступа", ВариантДоступаСтрокой);
		
		ТекстЗапросаПроверки =
		"ВЫБРАТЬ
		|	&ОпорныеПоляВыбораПриПроверке
		|ПОМЕСТИТЬ КлючиДоступаКРегистрам
		|ИЗ
		|	&КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	&ОпорныеПоляВыбораПриПроверке
		|ИЗ
		|	КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
		|ГДЕ
		|	НЕ ИСТИНА В
		|				(ВЫБРАТЬ ПЕРВЫЕ 1
		|					ИСТИНА
		|				ИЗ
		|					&ТекущийСписок КАК ТекущийСписок
		|				ГДЕ
		|					&УсловиеОпорныеПоляДляСравнения)
		|	И &УточнениеПланаЗапроса";
		
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки, "&ОпорныеПоляВыбораПриПроверке", ОпорныеПоляВыбораПриПроверке);
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки, "&УсловиеОпорныеПоляДляСравнения",
			ТекстСОтступом(ОпорныеПоляДляСравнения, "						"));
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийСписок", Результат.Список);
		ТекстЗапросаПроверки = СтрЗаменить(ТекстЗапросаПроверки, "&ТекущийСписок", Результат.Список);
		Результат.Вставить("ТекстЗапросаПроверкиУстаревшихЭлементовДанных", ТекстЗапросаПроверки);
	КонецЕсли;
	
	Результат.ТекстЗапросаУстаревшихЭлементовДанных = ТекстЗапроса;
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ЗаполнитьШаблоныЧастейЗапросаПроверки(Контекст)
	
	Если Контекст.ЭтоСсылочныйТип Тогда
		ЗаполнитьШаблоныЧастейЗапросаПроверкиОбъекта(Контекст);
	Иначе
		ЗаполнитьШаблоныЧастейЗапросаПроверкиРегистра(Контекст);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьШаблоныЧастейЗапросаПроверки.
Процедура ЗаполнитьШаблоныЧастейЗапросаПроверкиОбъекта(Контекст)
	
	Контекст.Вставить("ТекстЗапросаПроверки");
	Контекст.Вставить("ТекстЧастиЗапросаПроверки");
	Контекст.Вставить("ТекстЗапросаТочечнойПроверки");
	
	Если Контекст.СписокСДатой Тогда
		Контекст.ТекстЗапросаПроверки =
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка,
		|	ТекущийСписок.Дата КАК Дата
		|ИЗ
		|	&ТекущийСписок КАК ТекущийСписок
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
		|		ПО (КлючиДоступаКОбъектам.Объект = ТекущийСписок.Ссылка)
		|ГДЕ
		|	ТекущийСписок.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
		|	И (КлючиДоступаКОбъектам.Объект ЕСТЬ NULL
		|			ИЛИ &УсловиеЗапроса)
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Дата УБЫВ";
		
		Контекст.ТекстЧастиЗапросаПроверки = 
		"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка,
		|	ТекущийСписок.Дата КАК Дата
		|ИЗ
		|	&ТекущийСписок КАК ТекущийСписок
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
		|		ПО (КлючиДоступаКОбъектам.Объект = ТекущийСписок.Ссылка)
		|		#Соединения
		|ГДЕ
		|	ТекущийСписок.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
		|	И &УсловиеЗапроса
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Дата УБЫВ"; // @query-part-1
	Иначе
		Контекст.ТекстЗапросаПроверки = 
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка
		|ИЗ
		|	&ТекущийСписок КАК ТекущийСписок
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
		|		ПО (КлючиДоступаКОбъектам.Объект = ТекущийСписок.Ссылка)
		|ГДЕ
		|	ТекущийСписок.Ссылка >= &ПоследняяОбработаннаяСсылка
		|	И (КлючиДоступаКОбъектам.Объект ЕСТЬ NULL
		|			ИЛИ &УсловиеЗапроса)
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Ссылка"; // @query-part-1
		
		Контекст.ТекстЧастиЗапросаПроверки =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка
		|ИЗ
		|	&ТекущийСписок КАК ТекущийСписок
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
		|		ПО (КлючиДоступаКОбъектам.Объект = ТекущийСписок.Ссылка)
		|		#Соединения
		|ГДЕ
		|	ТекущийСписок.Ссылка >= &ПоследняяОбработаннаяСсылка
		|	И &УсловиеЗапроса
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущийСписок.Ссылка"; // @query-part-1
	КонецЕсли;
	
	Контекст.ТекстЗапросаТочечнойПроверки =
	"ВЫБРАТЬ
	|	ТекущийСписок.Ссылка КАК ТекущаяСсылка
	|ИЗ
	|	&ТекущийСписок КАК ТекущийСписок
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТекущийСписокПоВедущимОбъектам КАК ТекущийСписокПоВедущимОбъектам
	|		ПО (ТекущийСписокПоВедущимОбъектам.Ссылка = ТекущийСписок.Ссылка)
	|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
	|		ПО (КлючиДоступаКОбъектам.Объект = ТекущийСписок.Ссылка)
	|ГДЕ
	|	(КлючиДоступаКОбъектам.Объект ЕСТЬ NULL
	|			ИЛИ &УсловиеЗапроса)
	|	И &УточнениеПланаЗапроса";
	
	Если Не Контекст.Свойство("ОписаниеЗапросовПроверкиПоВедущимОбъектам") Тогда
		Возврат;
	КонецЕсли;
	
	ТекстЗапросаОберткиВыбораДанныхДляТочечнойПроверки =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
	|	ТекущийСписок.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ ТекущийСписокПоВедущимОбъектам
	|ИЗ
	|	#ЗапросыВыбораДанных КАК ТекущийСписок"; 
	
	Контекст.ОписаниеЗапросовПроверкиПоВедущимОбъектам.Вставить("ТекстЗапросаОберткиВыбораДанных",
		ТекстЗапросаОберткиВыбораДанныхДляТочечнойПроверки);
	
	ШаблонЗапроса =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
	|	ТекущийСписок.Ссылка КАК Ссылка
	|ИЗ
	|	"; // @query-part-1
	ДобавитьЗапросыПроверкиПоВедущимСпискам(ШаблонЗапроса, Контекст);
	
КонецПроцедуры

// Для процедуры ЗаполнитьШаблоныЧастейЗапросаПроверки.
Процедура ЗаполнитьШаблоныЧастейЗапросаПроверкиРегистра(Контекст)
	
	ОпорныеПоля = Контекст.ОпорныеПоля;
	ОпорныеПоля.Вставить("ДляВыбора",         "");
	ОпорныеПоля.Вставить("УсловиеСоединения", "");
	ОпорныеПоля.Вставить("ДляОтбора",         "");
	ОпорныеПоля.Вставить("ДляУпорядочения",   "");
	
	// Для запроса новых комбинаций.
	ОпорныеПоляДляВыбора = "";
	ОпорныеПоляУсловиеСоединения = "";
	ОпорныеПоляДляГруппировкиИлиУпорядочения = "";
	ОпорныеПоляДляОтбора = "";
	
	// Для запроса текущих ключей доступа.
	УсловиеОтбора = "";
	
	Если Не ЗначениеЗаполнено(Контекст.ИмяОтдельногоРегистраКлючей) Тогда
		Контекст.ОтдельныйРегистр = Ложь;
		// Для запроса новых комбинаций.
		ОпорныеПоляУсловиеСоединения  = "(КлючиДоступаКРегистрам.Регистр = &ИдентификаторРегистра)";
		// Для запроса текущих ключей доступа.
		УсловиеОтбора                 = "ТекущийСписок.Регистр = &ИдентификаторРегистра";
		// Для остальных запросов.
		ОпорныеПоля.УсловиеСоединения = "(ТекущийСписокИсточник.Регистр = ТекущийСписок.Регистр)";
		ОпорныеПоля.ДляОтбора         = "ТекущийСписок.Регистр = &ИдентификаторРегистра";
	КонецЕсли;
	
	// Для запроса новых комбинаций.
	ОпорныеПоляУсловиеСоединения = ОпорныеПоляУсловиеСоединения + ?(ОпорныеПоляУсловиеСоединения = "", "", "
	|			И ") + "(КлючиДоступаКРегистрам.ВариантДоступа = &ВариантДоступа)"; // @query-part-1
	
	// Для запроса текущих ключей доступа.
	УсловиеОтбора = УсловиеОтбора + ?(УсловиеОтбора = "", "", "
	|	И ") + "ТекущийСписок.ВариантДоступа = &ВариантДоступа"; // @query-part-1
	
	// Для остальных запросов.
	ОпорныеПоля.УсловиеСоединения = ОпорныеПоля.УсловиеСоединения + ?(ОпорныеПоля.УсловиеСоединения = "", "", "
	|	И ") + "(ТекущийСписокИсточник.ВариантДоступа = &ВариантДоступа)"; // @query-part-1
	
	ОпорныеПоля.ДляОтбора = ОпорныеПоля.ДляОтбора + ?(ОпорныеПоля.ДляОтбора = "", "", "
	|	И ") + "ТекущийСписок.ВариантДоступа = &ВариантДоступа"; // @query-part-1
	
	Номер = 0;
	Для Каждого ИмяОпорногоПоля Из ОпорныеПоля.Используемые Цикл
		Номер = Номер + 1;
		
		// Для запроса новых комбинаций.
		ОпорныеПоляДляВыбора = ОпорныеПоляДляВыбора + ?(ОпорныеПоляДляВыбора = "", "", ",
		|	") + "ТекущийРегистр." + ИмяОпорногоПоля + " КАК Поле" + Номер; // @query-part-3
		
		ОпорныеПоляУсловиеСоединения = ОпорныеПоляУсловиеСоединения + ?(ОпорныеПоляУсловиеСоединения = "", "", "
		|			И ") + "(КлючиДоступаКРегистрам.Поле" + Номер + " = ТекущийРегистр." + ИмяОпорногоПоля + ")"; // @query-part-1
		
		ОпорныеПоляДляГруппировкиИлиУпорядочения = ОпорныеПоляДляГруппировкиИлиУпорядочения
			+ ?(ОпорныеПоляДляГруппировкиИлиУпорядочения = "", "", ", ") + ИмяОпорногоПоля;
		
		Отбор = "";
		Для ДополнительныйНомер = 1 По Номер - 1 Цикл
			ТекущееИмяОпорногоПоля = ОпорныеПоля.Используемые[ДополнительныйНомер - 1];
			Отбор = Отбор + ?(ДополнительныйНомер = 1, "", "
			|	И ") + "ТекущийРегистр." + ТекущееИмяОпорногоПоля + " = &Поле" + ДополнительныйНомер; // @query-part-1
		КонецЦикла;
		Отбор = Отбор + ?(Отбор = "", "", "
		|	И ") + "ТекущийРегистр." + ИмяОпорногоПоля + " > &Поле" + Номер; // @query-part-1
		
		Отбор = ?(Номер = 1, ?(ОпорныеПоля.Используемые.Количество() > 1, "(", "") + Отбор, "
		|		ИЛИ " + ТекстСОтступом(Отбор, "		")); // @query-part-1
		
		ОпорныеПоляДляОтбора = ОпорныеПоляДляОтбора + ?(ОпорныеПоляДляОтбора = "", "", ?(Номер = 1, "
		|	И ", "")) + Отбор; // @query-part-1
		
		// Для запроса текущих ключей доступа.
		УсловиеОтбора = УсловиеОтбора + ?(УсловиеОтбора = "", "", "
		|	И ") + "ТекущийСписок.Поле" + Номер + " = &Поле" + Номер + "_%1"; // @query-part-1
		
		// Для остальных запросов.
		ОпорныеПоля.ДляВыбора = ОпорныеПоля.ДляВыбора + ?(ОпорныеПоля.ДляВыбора = "", "", ",
		|	") + "ТекущийСписок.Поле" + Номер + " КАК Поле" + Номер; // @query-part-3
		
		ОпорныеПоля.УсловиеСоединения = ОпорныеПоля.УсловиеСоединения + ?(ОпорныеПоля.УсловиеСоединения = "", "", "
		|	И ") + "(ТекущийСписокИсточник.Поле" + Номер + " = ТекущийСписок.Поле" + Номер + ")"; // @query-part-1
		
		Отбор = "";
		Для ДополнительныйНомер = 1 По Номер - 1 Цикл
			Отбор = Отбор + ?(ДополнительныйНомер = 1, "", "
			|		И ") + "ТекущийСписок.Поле" + ДополнительныйНомер + " = &Поле" + ДополнительныйНомер; // @query-part-1
		КонецЦикла;
		Отбор = Отбор + ?(Отбор = "", "", "
		|		И ") + "ТекущийСписок.Поле" + Номер + " > &Поле" + Номер; // @query-part-1
		
		Отбор = ?(Номер = 1, ?(ОпорныеПоля.Используемые.Количество() > 1, "(", "") + Отбор, "
		|			ИЛИ " + ТекстСОтступом(Отбор, "		")); // @query-part-1
		
		ОпорныеПоля.ДляОтбора = ОпорныеПоля.ДляОтбора + ?(ОпорныеПоля.ДляОтбора = "", "", ?(Номер = 1, "
		|	И ", "")) + Отбор; // @query-part-1
		
		ОпорныеПоля.ДляУпорядочения = ОпорныеПоля.ДляУпорядочения + ?(ОпорныеПоля.ДляУпорядочения = "", "", ", ") + "Поле" + Номер;
	КонецЦикла;
	
	Если ОпорныеПоля.Используемые.Количество() > 1 Тогда
		ОпорныеПоляДляОтбора  = ОпорныеПоляДляОтбора  + ")";
		ОпорныеПоля.ДляОтбора = ОпорныеПоля.ДляОтбора + ")";
	КонецЕсли;
	
	// Установка шаблонов запросов.
	ТекстЗапросаПроверкиКомбинаций =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
	|	&ОпорныеПоляДляВыбора
	|ИЗ
	|	&ТекущийСписок КАК ТекущийСписок
	|ГДЕ
	|	&ОпорныеПоляДляОтбора
	|	И (&УсловиеЗапроса)
	|	И &УточнениеПланаЗапроса
	|
	|УПОРЯДОЧИТЬ ПО
	|	&ОпорныеПоляДляУпорядочения"; 

	ТекстЗапросаПроверкиКомбинаций = СтрЗаменить(ТекстЗапросаПроверкиКомбинаций, "&ОпорныеПоляДляВыбора", ОпорныеПоля.ДляВыбора);
	ТекстЗапросаПроверкиКомбинаций = СтрЗаменить(ТекстЗапросаПроверкиКомбинаций, "&ОпорныеПоляДляОтбора", ОпорныеПоля.ДляОтбора);
	ТекстЗапросаПроверкиКомбинаций = СтрЗаменить(ТекстЗапросаПроверкиКомбинаций, "&ОпорныеПоляДляУпорядочения", ОпорныеПоля.ДляУпорядочения);
	Контекст.Вставить("ТекстЗапросаПроверки", ТекстЗапросаПроверкиКомбинаций);
	
	ТекстЗапросаТочечнойПроверкиКомбинаций =
	"ВЫБРАТЬ
	|	&ОпорныеПоляДляВыбора
	|ИЗ
	|	ТекущийСписокПоВедущимОбъектам КАК ТекущийСписок
	|ГДЕ
	|	(&УсловиеЗапроса)
	|	И &УточнениеПланаЗапроса";
	
	ТекстЗапросаТочечнойПроверкиКомбинаций = СтрЗаменить(ТекстЗапросаТочечнойПроверкиКомбинаций, 
		"&ОпорныеПоляДляВыбора", ОпорныеПоля.ДляВыбора);
	Контекст.Вставить("ТекстЗапросаТочечнойПроверки", ТекстЗапросаТочечнойПроверкиКомбинаций);
	
	ТекстЗапросаОберткиВыбораДанныхДляТочечнойПроверкиКомбинаций =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
	|	&ОпорныеПоляДляВыбора,
	|	ТекущийСписок.КлючДоступа КАК КлючДоступа
	|ПОМЕСТИТЬ ТекущийСписокПоВедущимОбъектам
	|ИЗ
	|	#ЗапросыВыбораДанных КАК ТекущийСписок";
	
	ТекстЗапросаОберткиВыбораДанныхДляТочечнойПроверкиКомбинаций = СтрЗаменить(ТекстЗапросаОберткиВыбораДанныхДляТочечнойПроверкиКомбинаций, 
		"&ОпорныеПоляДляВыбора", ОпорныеПоля.ДляВыбора);
	Контекст.ОписаниеЗапросовПроверкиПоВедущимОбъектам.Вставить("ТекстЗапросаОберткиВыбораДанных",
		ТекстЗапросаОберткиВыбораДанныхДляТочечнойПроверкиКомбинаций);
	
	ШаблонЗапроса =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
	|	&ОпорныеПоляДляВыбора,
	|	ТекущийСписок.КлючДоступа КАК КлючДоступа
	|ИЗ
	|	"; // @query-part-1
	ШаблонЗапроса = СтрЗаменить(ШаблонЗапроса, "&ОпорныеПоляДляВыбора", ОпорныеПоля.ДляВыбора);
	ДобавитьЗапросыПроверкиПоВедущимСпискам(ШаблонЗапроса, Контекст);
	
	Если Контекст.СписокСПериодом Тогда
		ТекстЗапросаНовыхКомбинаций = 
		"ВЫБРАТЬ ПЕРВЫЕ 995
		|	МАКСИМУМ(ТекущийРегистр.Период) КАК Период,
		|	&ОпорныеПоляДляВыбора
		|ИЗ
		|	&ТекущийРегистр КАК ТекущийРегистр
		|		ЛЕВОЕ СОЕДИНЕНИЕ &ТекущийСписок КАК КлючиДоступаКРегистрам
		|		ПО &ОпорныеПоляУсловиеСоединения
		|ГДЕ
		|	ТекущийРегистр.Период >= &ДатаНачала
		|	И (ТекущийРегистр.Период < &ДатаОкончания
		|		ИЛИ ТекущийРегистр.Период = &ДатаОкончания
		|			И &ОпорныеПоляДляОтбора)
		|	И КлючиДоступаКРегистрам.ВариантДоступа ЕСТЬ NULL
		|	И &УточнениеПланаЗапроса
		|
		|СГРУППИРОВАТЬ ПО
		|	&ОпорныеПоляДляГруппировкиИлиУпорядочения
		|
		|УПОРЯДОЧИТЬ ПО
		|	Период УБЫВ, &ОпорныеПоляДляГруппировкиИлиУпорядочения";
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляДляВыбора", ОпорныеПоляДляВыбора);
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляУсловиеСоединения", ОпорныеПоляУсловиеСоединения);
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляДляОтбора", ТекстСОтступом(ОпорныеПоляДляОтбора, "	"));
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляДляГруппировкиИлиУпорядочения", ОпорныеПоляДляГруппировкиИлиУпорядочения);
		Если Контекст.ИмяКоллекцииТипа = "РегистрыРасчета" Тогда
			ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций,
				".Период", ".ПериодРегистрации");
		КонецЕсли;
	Иначе
		ТекстЗапросаНовыхКомбинаций =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ ПЕРВЫЕ 995
		|	&ОпорныеПоляДляВыбора
		|ИЗ
		|	&ТекущийРегистр КАК ТекущийРегистр
		|		ЛЕВОЕ СОЕДИНЕНИЕ &ТекущийСписок КАК КлючиДоступаКРегистрам
		|		ПО &ОпорныеПоляУсловиеСоединения
		|ГДЕ
		|	&ОпорныеПоляДляОтбора
		|	И КлючиДоступаКРегистрам.ВариантДоступа ЕСТЬ NULL
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	&ОпорныеПоляДляГруппировкиИлиУпорядочения";
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляДляВыбора", ОпорныеПоляДляВыбора);
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляУсловиеСоединения", ОпорныеПоляУсловиеСоединения);
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляДляОтбора", ОпорныеПоляДляОтбора);
		ТекстЗапросаНовыхКомбинаций = СтрЗаменить(ТекстЗапросаНовыхКомбинаций, 
			"&ОпорныеПоляДляГруппировкиИлиУпорядочения", ОпорныеПоляДляГруппировкиИлиУпорядочения);
	КонецЕсли;
	Контекст.Вставить("ТекстЗапросаНовыхКомбинаций", ТекстЗапросаНовыхКомбинаций);
	
	// Текст запроса текущих ключей доступа.
	ТекстЗапросаТекущихКлючейДоступа =
	"ВЫБРАТЬ ПЕРВЫЕ 2
	|	ТекущийСписок.КлючДоступа КАК КлючДоступа
	|ИЗ
	|	&ТекущийСписок КАК ТекущийСписок
	|ГДЕ
	|	&УсловиеОтбора";
	ТекстЗапросаТекущихКлючейДоступа = СтрЗаменить(ТекстЗапросаТекущихКлючейДоступа, "&УсловиеОтбора", УсловиеОтбора);
	Контекст.Вставить("ТекстЗапросаТекущихКлючейДоступа", ТекстЗапросаТекущихКлючейДоступа);
	
КонецПроцедуры

// Для процедур ЗаполнитьШаблоныЧастейЗапросаПроверкиОбъекта, ЗаполнитьШаблоныЧастейЗапросаПроверкиРегистра.
Процедура ДобавитьЗапросыПроверкиПоВедущимСпискам(ШаблонЗапроса, Контекст)
	
	ОписаниеЗапросов = Новый Соответствие;
	Для Каждого СоединениеОтбора Из Контекст.ВедущиеСпискиПоЗначениямПолей.СоединенияОтборов Цикл
		ОписаниеСоединения = СоединениеОтбора.Значение;
		Поля = Контекст.ВедущиеСпискиПоЗначениямПолей.Поля.Получить(СоединениеОтбора.Ключ);
		ВедущаяТаблица = СоединениеОтбора.Ключ;
		Если ОписаниеСоединения.ПоляШапки.Количество() > 0 Тогда
			ОписаниеЗапросов.Вставить(ВедущаяТаблица,
				ОписаниеЗапросовПроверкиПоВедущейТаблице(ОписаниеСоединения.ПоляШапки,
					Поля.ДляОтбора.ПоляШапки, ВедущаяТаблица, ШаблонЗапроса, Контекст));
		КонецЕсли;
		Для Каждого ОписаниеТабличнойЧасти Из ОписаниеСоединения.ТабличныеЧасти Цикл
			ВедущаяТаблица = СоединениеОтбора.Ключ + "." + ОписаниеТабличнойЧасти.Ключ;
			ТипыПолей = Поля.ДляОтбора.ТабличныеЧасти.Получить(ОписаниеТабличнойЧасти.Ключ);
			ОписаниеЗапросов.Вставить(ВедущаяТаблица,
				ОписаниеЗапросовПроверкиПоВедущейТаблице(ОписаниеТабличнойЧасти.Значение,
					ТипыПолей, ВедущаяТаблица, ШаблонЗапроса, Контекст));
		КонецЦикла;
	КонецЦикла;
	Контекст.ОписаниеЗапросовПроверкиПоВедущимОбъектам.Вставить("ПоЗначениямПолей", ОписаниеЗапросов);
	
	ДобавитьЗапросыПроверкиПоПолюСсылкаВедущихСписков("ПоЗначениямСГруппами", ШаблонЗапроса, Контекст);
	ДобавитьЗапросыПроверкиПоПолюСсылкаВедущихСписков("ПоКлючамДоступа", ШаблонЗапроса, Контекст);
	
КонецПроцедуры

// Для процедуры ДобавитьЗапросыПроверкиПоВедущимСпискам.
Функция ОписаниеЗапросовПроверкиПоВедущейТаблице(СоединенияОтборов, ТипыПолей,
			ВедущаяТаблица, ШаблонЗапроса, Контекст)
	
	ВедущаяТаблицаБезТочек = СтрЗаменить(ВедущаяТаблица, ".", "_");
	
	Поля = Новый Массив;
	Для Каждого ОписаниеПоля Из ТипыПолей Цикл
		Поля.Добавить(ОписаниеПоля.Ключ + " КАК " + ОписаниеПоля.Ключ);
	КонецЦикла;
	
	ТекстЗапросаПараметров =
	"ВЫБРАТЬ
	|	&ПоляТаблицы
	|ПОМЕСТИТЬ #ВедущаяТаблицаБезТочек
	|ИЗ
	|	&ВедущаяТаблицаБезТочек КАК ТекущиеДанныеДляОтбора";
	ТекстЗапросаПараметров = СтрЗаменить(ТекстЗапросаПараметров, "&ПоляТаблицы", "
		|	ТекущиеДанныеДляОтбора." + СтрСоединить(Поля, ",
		|	ТекущиеДанныеДляОтбора."));
	ТекстЗапросаПараметров = СтрЗаменить(ТекстЗапросаПараметров, "#ВедущаяТаблицаБезТочек", ВедущаяТаблицаБезТочек);
	ТекстЗапросаПараметров = СтрЗаменить(ТекстЗапросаПараметров, "&ВедущаяТаблицаБезТочек", "&" + ВедущаяТаблицаБезТочек);
	
	ОписаниеЗапросов = Новый Структура;
	ОписаниеЗапросов.Вставить("ТипыПолей", ТипыПолей);
	ОписаниеЗапросов.Вставить("ТекстЗапросаПараметров", ТекстЗапросаПараметров);
	ОписаниеЗапросов.Вставить("ТекстыЗапросовДанных", Новый Массив);
	
	Для Каждого СоединениеОтбора Из СоединенияОтборов Цикл
		ТекстЗапроса = ШаблонЗапроса + ТекстСОтступом(СоединениеОтбора, "	");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТекущиеДанныеДляОтбора", ВедущаяТаблицаБезТочек);
		ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
		ОписаниеЗапросов.ТекстыЗапросовДанных.Добавить(ТекстЗапроса);
	КонецЦикла;
	
	Возврат ОписаниеЗапросов;
	
КонецФункции

// Для процедуры ДобавитьЗапросыПроверкиПоВедущимСпискам.
Процедура ДобавитьЗапросыПроверкиПоПолюСсылкаВедущихСписков(ВидВедущегоСписка, ШаблонЗапроса, Контекст)
	
	Свойства = ?(ВидВедущегоСписка = "ПоКлючамДоступа",
		Контекст.ВедущиеСпискиПоКлючамДоступа, Контекст.ВедущиеСпискиПоЗначениямСГруппами);
	
	Если Не ЗначениеЗаполнено(Свойства.СоединенияОтборов) Тогда
		Возврат;
	КонецЕсли;
	
	ИмяВременнойТаблицы = "ТекущиеДанныеДляОтбора" + ВидВедущегоСписка;
	
	ТекстЗапросаПараметров =
	"ВЫБРАТЬ
	|	ТекущиеДанныеДляОтбора.Ссылка
	|ПОМЕСТИТЬ #ИмяВременнойТаблицы
	|ИЗ
	|	&ВидВедущегоСписка КАК ТекущиеДанныеДляОтбора";
	ТекстЗапросаПараметров = СтрЗаменить(ТекстЗапросаПараметров, "#ИмяВременнойТаблицы", ИмяВременнойТаблицы);
	ТекстЗапросаПараметров = СтрЗаменить(ТекстЗапросаПараметров, "&ВидВедущегоСписка", "&" + ВидВедущегоСписка);

	ТекстыЗапросовПоКлючам = Новый Соответствие;
	Для Каждого СоединениеОтбора Из Свойства.СоединенияОтборов Цикл
		ТекстЗапроса = ШаблонЗапроса + ТекстСОтступом(СоединениеОтбора.Значение, "	");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ТекущиеДанныеДляОтбора", ИмяВременнойТаблицы);
		ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
		ТекстыЗапросовПоКлючам.Вставить(СоединениеОтбора.Ключ, ТекстЗапроса);
	КонецЦикла;
	
	ОписаниеЗапросов = Новый Структура;
	ОписаниеЗапросов.Вставить("ТипСсылки",              Новый ХранилищеЗначения(Свойства.ТипСсылки));
	ОписаниеЗапросов.Вставить("КлючиЗапросовПоТипам",   Свойства.КлючиЗапросовПоТипам);
	ОписаниеЗапросов.Вставить("ТекстыЗапросовПоКлючам", ТекстыЗапросовПоКлючам);
	ОписаниеЗапросов.Вставить("ТекстЗапросаПараметров", ТекстЗапросаПараметров);
	
	Контекст.ОписаниеЗапросовПроверкиПоВедущимОбъектам.Вставить(ВидВедущегоСписка, ОписаниеЗапросов);
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура СобратьЧастиЗапросаПроверки(Результат, Контекст)
	
	Условие = "";
	Для Каждого ТекстЧастиУсловия Из Контекст.ЧастиУсловияПроверки Цикл
		Если ЗначениеЗаполнено(Условие) Тогда
			Условие = Условие + "
			|		ИЛИ "; // @query-part-1
		КонецЕсли;
		Условие = Условие + ТекстСОтступом(ТекстЧастиУсловия, "			");
	КонецЦикла;
	Условие = ТекстСОтступом(Условие, "	");
	
	Контекст.ТекстЗапросаПроверки = СтрЗаменить(Контекст.ТекстЗапросаПроверки,
		"&УсловиеЗапроса", ?(Контекст.ЭтоСсылочныйТип, Условие, ТекстСОтступом(Условие, "		")));
	ПодставитьОбщиеПараметрыВЗапрос(Контекст.ТекстЗапросаПроверки, Контекст);
	Результат.ТекстЗапросаЭлементовДанныхСУстаревшимиКлючами = Контекст.ТекстЗапросаПроверки;
	
	ТекстЗапроса = СтрЗаменить(Контекст.ТекстЗапросаТочечнойПроверки, "&УсловиеЗапроса", Условие);
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Контекст.ОписаниеЗапросовПроверкиПоВедущимОбъектам.Вставить("ТекстЗапросаТочечнойПроверки", ТекстЗапроса);
	
	Результат.ОписаниеЗапросовУстаревшихКлючейДоступаПоВедущимОбъектам =
		Контекст.ОписаниеЗапросовПроверкиПоВедущимОбъектам;
	
	Если Не Контекст.ЭтоСсылочныйТип Тогда
		ПодставитьОбщиеПараметрыВЗапрос(Контекст.ТекстЗапросаНовыхКомбинаций, Контекст);
		Результат.ТекстЗапросаЭлементовДанныхБезКлючейДоступа = Контекст.ТекстЗапросаНовыхКомбинаций;
		
		ПодставитьОбщиеПараметрыВЗапрос(Контекст.ТекстЗапросаТекущихКлючейДоступа, Контекст);
		Результат.ТекстЗапросаТекущихКлючейДоступаРегистра = Контекст.ТекстЗапросаТекущихКлючейДоступа;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура СобратьЧастиЗапросовЗаполнения(Результат, Контекст)
	
	ТекстЗапроса = СтрСоединить(Контекст.ЧастиЗапросаЗначенийИзОбъектов,
		ОбщегоНазначения.РазделительПакетаЗапросов());
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Результат.ТекстЗапросаЗначенийЭлементовДанныхДляКлючейДоступа = ТекстЗапроса;
	
	Если ЗначениеЗаполнено(Контекст.ЧастиЗапросаЗначенийИзОбъектовВПамяти) Тогда
		ТекстЗапроса = СтрСоединить(Контекст.ЧастиЗапросаЗначенийИзОбъектовВПамяти,
			ОбщегоНазначения.РазделительПакетаЗапросов());
		ИмяВременнойТаблицы = СтрЗаменить(Контекст.Список, ".", "_");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийСписок.", ИмяВременнойТаблицы + "_");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийСписок", ИмяВременнойТаблицы);
		ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
		Результат.ТекстЗапросаЗначенийОбъектовВПамятиДляКлючейДоступа = ТекстЗапроса;
	КонецЕсли;
	
	ТекстЗапроса = СтрСоединить(Контекст.ЧастиЗапросаЗначенийИзКлючейДляСравнения,
		ОбщегоНазначения.РазделительПакетаЗапросов());
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Результат.ТекстЗапросаЗначенийИзИспользуемыхКлючейДоступаДляСравнения = ТекстЗапроса;
	Результат.ТекстЗапросаЗначенийИзВсехКлючейДоступаДляСравнения = СтрЗаменить(ТекстЗапроса,
		".НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)", ".Список = &Список"); // @query-part-1
	
	ТекстЗапроса = СтрСоединить(Контекст.ЧастиЗапросаСуществованияКлючейДляСравнения,
		ОбщегоНазначения.РазделительПакетаЗапросов());
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Результат.ТекстЗапросаСуществованияКлючейДляСравнения = ТекстЗапроса;
	
	ДобавитьЧастьЗапросаВыбораПравВедущихКлючейДоступа(Контекст);
	ДобавитьЧастьЗапросаВыбораПравВедущихСписков(Контекст);
	ДобавитьЧастьЗапросаВыбораПравПоВладельцамНастроекПрав(Контекст);
	ТекстЗапроса = СтрСоединить(Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав,
		ОбщегоНазначения.РазделительПакетаЗапросов());
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Результат.ТекстЗапросаЗначенийИзКлючейДоступаДляРасчетаПрав = ТекстЗапроса;
	
	Результат.ТекстЗапросаКлючейДоступаДляОбновленияПрав =
		ТекстЗапросаКлючейДоступаДляОбновленияПрав(Контекст);
	
	Результат.ТекстЗапросаКлючейПоВедущимКлючамДляОбновленияПрав =
		ТекстЗапросаКлючейПоВедущимКлючамДляОбновленияПрав(Контекст);
	
	Результат.ТекстЗапросаУстаревшихКлючейДоступа =
		ТекстЗапросаУстаревшихКлючейДоступа(Контекст);
	
КонецПроцедуры

// Для процедуры СобратьЧастиЗапросовЗаполнения.
Процедура ДобавитьЧастьЗапросаВыбораПравВедущихКлючейДоступа(Контекст)
	
	Если Контекст.ЧастиУсловияВыбораПравВедущихКлючейДоступа.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если Контекст.ЧастиУсловияВыбораПравВедущихКлючейДоступа.Количество() = 1 Тогда
		УсловиеОтбора = Контекст.ЧастиУсловияВыбораПравВедущихКлючейДоступа[0];
	Иначе
		УсловиеОтбора = "(" + СтрСоединить(Контекст.ЧастиУсловияВыбораПравВедущихКлючейДоступа,
			Символы.ПС + "			ИЛИ ") + ")"; // @query-part-1
	КонецЕсли;
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ПраваНаСпискиВедущихКлючейДоступа.Таблица КАК Список,
	|	ПраваНаСпискиВедущихКлючейДоступа.ГруппаДоступа КАК ГруппаДоступа,
	|	ПраваНаСпискиВедущихКлючейДоступа.ПравоИзменение КАК ПравоИзменение
	|ИЗ
	|	РегистрСведений.ТаблицыГруппДоступа КАК ПраваНаСпискиВедущихКлючейДоступа
	|ГДЕ
	|	ПраваНаСпискиВедущихКлючейДоступа.Таблица В
	|			(ВЫБРАТЬ РАЗЛИЧНЫЕ
	|				СпискиВедущихКлючейДоступа.Список
	|			ИЗ
	|				Справочник.КлючиДоступа КАК СпискиВедущихКлючейДоступа
	|			ГДЕ
	|				&УсловиеОтбора)
	|	И &УточнениеПланаЗапроса
	|ИТОГИ ПО
	|	Список";
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора",
		СтрЗаменить(УсловиеОтбора, "#ПроверяемоеПоле", "СпискиВедущихКлючейДоступа.Ссылка"));
	
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	СпискиВедущихКлючейДоступа.Ссылка КАК КлючДоступа,
	|	СпискиВедущихКлючейДоступа.Список КАК Список
	|ИЗ
	|	Справочник.КлючиДоступа КАК СпискиВедущихКлючейДоступа
	|ГДЕ
	|	&УсловиеОтбора
	|	И &УточнениеПланаЗапроса";
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора",
		СтрЗаменить(УсловиеОтбора, "#ПроверяемоеПоле", "СпискиВедущихКлючейДоступа.Ссылка"));
	
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
	Если Не Контекст.РассчитыватьПраваПользователей Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ПраваНаКлючиДоступа.КлючДоступа КАК КлючДоступа,
		|	ПраваНаКлючиДоступа.ГруппаДоступа КАК ВладелецПрав,
		|	ПраваНаКлючиДоступа.ПравоИзменение КАК ПравоИзменение
		|ИЗ
		|	РегистрСведений.КлючиДоступаГруппДоступа КАК ПраваНаКлючиДоступа
		|ГДЕ
		|	ТИПЗНАЧЕНИЯ(ПраваНаКлючиДоступа.ГруппаДоступа) = ТИП(Справочник.ГруппыДоступа)
		|	И &УсловиеОтбора
		|	И &УточнениеПланаЗапроса
		|ИТОГИ ПО
		|	КлючДоступа";
	ИначеЕсли Не Контекст.ДляВнешнихПользователей Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ПраваНаКлючиДоступа.КлючДоступа КАК КлючДоступа,
		|	ПраваНаКлючиДоступа.ГруппаДоступа КАК ВладелецПрав,
		|	ПраваНаКлючиДоступа.ПравоИзменение КАК ПравоИзменение
		|ИЗ
		|	РегистрСведений.КлючиДоступаГруппДоступа КАК ПраваНаКлючиДоступа
		|ГДЕ
		|	&УсловиеОтбора
		|	И &УточнениеПланаЗапроса
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ПраваНаКлючиДоступа.КлючДоступа,
		|	ПраваНаКлючиДоступа.Пользователь.Пользователь,
		|	ПраваНаКлючиДоступа.ПравоИзменение
		|ИЗ
		|	РегистрСведений.КлючиДоступаПользователей КАК ПраваНаКлючиДоступа
		|ГДЕ
		|	ПраваНаКлючиДоступа.ЭтоПраваНабораГрупп = ЛОЖЬ
		|	И ЕСТЬNULL(ПраваНаКлючиДоступа.Пользователь.Пользователь, ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)) <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
		|	И &УсловиеОтбора
		|ИТОГИ ПО
		|	КлючДоступа";
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ПраваНаКлючиДоступа.КлючДоступа КАК КлючДоступа,
		|	ПраваНаКлючиДоступа.ГруппаДоступа КАК ВладелецПрав,
		|	ПраваНаКлючиДоступа.ПравоИзменение КАК ПравоИзменение
		|ИЗ
		|	РегистрСведений.КлючиДоступаГруппДоступа КАК ПраваНаКлючиДоступа
		|ГДЕ
		|	&УсловиеОтбора
		|	И &УточнениеПланаЗапроса
		|
		|ОБЪЕДИНИТЬ ВСЕ
		|
		|ВЫБРАТЬ
		|	ПраваНаКлючиДоступа.КлючДоступа,
		|	ПраваНаКлючиДоступа.ВнешнийПользователь.Пользователь,
		|	ПраваНаКлючиДоступа.ПравоИзменение
		|ИЗ
		|	РегистрСведений.КлючиДоступаВнешнихПользователей КАК ПраваНаКлючиДоступа
		|ГДЕ
		|	ПраваНаКлючиДоступа.ЭтоПраваНабораГрупп = ЛОЖЬ
		|	И ЕСТЬNULL(ПраваНаКлючиДоступа.ВнешнийПользователь.Пользователь, ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)) <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка)
		|	И &УсловиеОтбора
		|ИТОГИ ПО
		|	КлючДоступа";
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора",
		СтрЗаменить(УсловиеОтбора, "#ПроверяемоеПоле", "ПраваНаКлючиДоступа.КлючДоступа"));
	
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
КонецПроцедуры

// Для процедуры СобратьЧастиЗапросовЗаполнения.
Процедура ДобавитьЧастьЗапросаВыбораПравВедущихСписков(Контекст)
	
	Если Контекст.ЧастиУсловияВыбораПравВедущихСписков.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если Контекст.Свойство("ЧастиУсловияВыбораПравВедущихСписковСТипами") Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ПраваНаСписки.Таблица КАК Список,
		|	ТИПЗНАЧЕНИЯ(ПраваНаСписки.Таблица.ЗначениеПустойСсылки) КАК ТипЗначения,
		|	ПраваНаСписки.ГруппаДоступа КАК ВладелецПрав,
		|	ПраваНаСписки.ПравоИзменение КАК ПравоИзменение
		|ИЗ
		|	РегистрСведений.ТаблицыГруппДоступа КАК ПраваНаСписки
		|ГДЕ
		|	&УсловиеОтбора
		|	И &УточнениеПланаЗапроса
		|ИТОГИ ПО
		|	Список";
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ПраваНаСписки.Таблица КАК Список,
		|	ПраваНаСписки.ГруппаДоступа КАК ВладелецПрав,
		|	ПраваНаСписки.ПравоИзменение КАК ПравоИзменение
		|ИЗ
		|	РегистрСведений.ТаблицыГруппДоступа КАК ПраваНаСписки
		|ГДЕ
		|	&УсловиеОтбора
		|	И &УточнениеПланаЗапроса
		|ИТОГИ ПО
		|	Список";
	КонецЕсли;
	
	Если Контекст.ЧастиУсловияВыбораПравВедущихСписков.Количество() = 1 Тогда
		УсловиеОтбора = Контекст.ЧастиУсловияВыбораПравВедущихСписков[0];
	Иначе
		УсловиеОтбора = "(" + СтрСоединить(Контекст.ЧастиУсловияВыбораПравВедущихСписков,
			Символы.ПС + "			ИЛИ ") + ")"; // @query-part-1
	КонецЕсли;
	УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "#ПроверяемоеПоле", "ПраваНаСписки.Таблица");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", УсловиеОтбора);
	
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
КонецПроцедуры

// Для процедуры СобратьЧастиЗапросовЗаполнения.
Процедура ДобавитьЧастьЗапросаВыбораПравПоВладельцамНастроекПрав(Контекст)
	
	Если Контекст.ЧастиУсловияВыбораПравПоВладельцамНастроекПрав.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ТекстЗапроса =
	"ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	НаследованиеНастроек.Объект КАК Объект
	|ПОМЕСТИТЬ ОбъектыСЗапрещениемЧтения
	|ИЗ
	|	РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|		ПО НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|			И (НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньЗапрещенияЧтения)
	|			И (&УсловиеОтбора)
	|			И (&УточнениеПланаЗапроса)
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Объект
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	НаследованиеНастроек.Объект КАК Объект
	|ПОМЕСТИТЬ ОбъектыСЗапрещениемИзменения
	|ИЗ
	|	РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|		ПО НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|			И (НастройкиПрав.Таблица = &ИдентификаторТаблицыНастроекПрав)
	|			И (НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньЗапрещенияИзменения)
	|			И (&УсловиеОтбора)
	|			И (&УточнениеПланаЗапроса)
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Объект
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НастройкиПравЧтения.Объект КАК Объект,
	|	НастройкиПравЧтения.Пользователь КАК Пользователь
	|ПОМЕСТИТЬ НастройкиПравЧтения
	|ИЗ
	|	(ВЫБРАТЬ РАЗЛИЧНЫЕ
	|		НаследованиеНастроек.Объект КАК Объект,
	|		ВЫБОР
	|			КОГДА НаследованиеНастроек.Объект В
	|					(ВЫБРАТЬ
	|						ОбъектыСЗапрещениемЧтения.Объект
	|					ИЗ
	|						ОбъектыСЗапрещениемЧтения)
	|				ТОГДА СоставыГруппПользователей.Пользователь
	|			ИНАЧЕ СоставыГруппПользователей.ГруппаПользователей
	|		КОНЕЦ КАК Пользователь,
	|		ИСТИНА КАК ЧтениеРазрешено,
	|		ЛОЖЬ КАК ЧтениеЗапрещено
	|	ИЗ
	|		РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|			ПО НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|				И (НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньРазрешенияЧтения)
	|				И (&УсловиеОтбора)
	|				И (&УточнениеПланаЗапроса)
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|			ПО (СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|				И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.Пользователь) = ТИП(Справочник.Пользователи))
	|				И (СоставыГруппПользователей.Используется)
	|				И (СоставыГруппПользователей.Пользователь.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ РАЗЛИЧНЫЕ
	|		НаследованиеНастроек.Объект,
	|		СоставыГруппПользователей.Пользователь,
	|		ЛОЖЬ,
	|		ИСТИНА
	|	ИЗ
	|		РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|			ПО (НаследованиеНастроек.Объект В
	|					(ВЫБРАТЬ
	|						ОбъектыСЗапрещениемЧтения.Объект
	|					ИЗ
	|						ОбъектыСЗапрещениемЧтения))
	|				И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|				И (НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньЗапрещенияЧтения)
	|				И (&УсловиеОтбора)
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|			ПО (СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|				И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.Пользователь) = ТИП(Справочник.Пользователи))
	|				И (СоставыГруппПользователей.Используется)
	|				И (СоставыГруппПользователей.Пользователь.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)) КАК НастройкиПравЧтения
	|
	|СГРУППИРОВАТЬ ПО
	|	НастройкиПравЧтения.Объект,
	|	НастройкиПравЧтения.Пользователь
	|
	|ИМЕЮЩИЕ
	|	МАКСИМУМ(НастройкиПравЧтения.ЧтениеРазрешено) = ИСТИНА И
	|	МАКСИМУМ(НастройкиПравЧтения.ЧтениеЗапрещено) = ЛОЖЬ
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НастройкиПравИзменения.Объект КАК Объект,
	|	НастройкиПравИзменения.Пользователь КАК Пользователь
	|ПОМЕСТИТЬ НастройкиПравИзменения
	|ИЗ
	|	(ВЫБРАТЬ РАЗЛИЧНЫЕ
	|		НаследованиеНастроек.Объект КАК Объект,
	|		ВЫБОР
	|			КОГДА НаследованиеНастроек.Объект В
	|					(ВЫБРАТЬ
	|						ОбъектыСЗапрещениемИзменения.Объект
	|					ИЗ
	|						ОбъектыСЗапрещениемИзменения)
	|				ТОГДА СоставыГруппПользователей.Пользователь
	|			ИНАЧЕ СоставыГруппПользователей.ГруппаПользователей
	|		КОНЕЦ КАК Пользователь,
	|		ИСТИНА КАК ИзменениеРазрешено,
	|		ЛОЖЬ КАК ИзменениеЗапрещено
	|	ИЗ
	|		РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|			ПО НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|				И (НастройкиПрав.Таблица = &ИдентификаторТаблицыНастроекПрав)
	|				И (НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньРазрешенияИзменения)
	|				И (&УсловиеОтбора)
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|			ПО (СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|				И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.Пользователь) = ТИП(Справочник.Пользователи))
	|				И (СоставыГруппПользователей.Используется)
	|				И (СоставыГруппПользователей.Пользователь.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ НастройкиПравЧтения КАК НастройкиПравЧтения
	|			ПО (НастройкиПравЧтения.Объект = НаследованиеНастроек.Объект)
	|				И (НастройкиПравЧтения.Пользователь = СоставыГруппПользователей.ГруппаПользователей
	|					ИЛИ НастройкиПравЧтения.Пользователь = СоставыГруппПользователей.Пользователь)
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ РАЗЛИЧНЫЕ
	|		НаследованиеНастроек.Объект,
	|		СоставыГруппПользователей.Пользователь,
	|		ЛОЖЬ,
	|		ИСТИНА
	|	ИЗ
	|		РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|			ПО (НаследованиеНастроек.Объект В
	|					(ВЫБРАТЬ
	|						ОбъектыСЗапрещениемИзменения.Объект
	|					ИЗ
	|						ОбъектыСЗапрещениемИзменения))
	|				И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|				И (НастройкиПрав.Таблица = &ИдентификаторТаблицыНастроекПрав)
	|				И (НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньЗапрещенияИзменения)
	|				И (&УсловиеОтбора)
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|			ПО (СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|				И (ТИПЗНАЧЕНИЯ(СоставыГруппПользователей.Пользователь) = ТИП(Справочник.Пользователи))
	|				И (СоставыГруппПользователей.Используется)
	|				И (СоставыГруппПользователей.Пользователь.ИдентификаторПользователяИБ <> &ПустойУникальныйИдентификатор)
	|			ВНУТРЕННЕЕ СОЕДИНЕНИЕ НастройкиПравЧтения КАК НастройкиПравЧтения
	|			ПО (НастройкиПравЧтения.Объект = НаследованиеНастроек.Объект)
	|				И (НастройкиПравЧтения.Пользователь = СоставыГруппПользователей.ГруппаПользователей
	|					ИЛИ НастройкиПравЧтения.Пользователь = СоставыГруппПользователей.Пользователь)) КАК НастройкиПравИзменения
	|
	|СГРУППИРОВАТЬ ПО
	|	НастройкиПравИзменения.Объект,
	|	НастройкиПравИзменения.Пользователь
	|
	|ИМЕЮЩИЕ
	|	МАКСИМУМ(НастройкиПравИзменения.ИзменениеРазрешено) = ИСТИНА И
	|	МАКСИМУМ(НастройкиПравИзменения.ИзменениеЗапрещено) = ЛОЖЬ
	|;
	|
	|////////////////////////////////////////////////////////////////////////////////
	|ВЫБРАТЬ
	|	НастройкиПрав.Объект КАК ВладелецНастроекПрав,
	|	НастройкиПрав.Пользователь КАК ВладелецПрав,
	|	МАКСИМУМ(НастройкиПрав.ПравоИзменение) КАК ПравоИзменение
	|ИЗ
	|	(ВЫБРАТЬ
	|		НастройкиПравЧтения.Объект КАК Объект,
	|		НастройкиПравЧтения.Пользователь КАК Пользователь,
	|		ЛОЖЬ КАК ПравоИзменение
	|	ИЗ
	|		НастройкиПравЧтения КАК НастройкиПравЧтения
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ
	|		НастройкиПравИзменения.Объект,
	|		НастройкиПравИзменения.Пользователь,
	|		ИСТИНА
	|	ИЗ
	|		НастройкиПравИзменения КАК НастройкиПравИзменения) КАК НастройкиПрав
	|
	|СГРУППИРОВАТЬ ПО
	|	НастройкиПрав.Объект,
	|	НастройкиПрав.Пользователь
	|ИТОГИ ПО
	|	ВладелецНастроекПрав";
	
	Если Контекст.ДляВнешнихПользователей Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "Справочник.Пользователи", "Справочник.ВнешниеПользователи");
	КонецЕсли;
	
	Если Контекст.ЧастиУсловияВыбораПравПоВладельцамНастроекПрав.Количество() = 1 Тогда
		УсловиеОтбора = Контекст.ЧастиУсловияВыбораПравПоВладельцамНастроекПрав[0];
	Иначе
		УсловиеОтбора =  СтрСоединить(Контекст.ЧастиУсловияВыбораПравПоВладельцамНастроекПрав,
			Символы.ПС + "			ИЛИ "); // @query-part-1
	КонецЕсли;
	УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "#ПроверяемоеПоле", "НаследованиеНастроек.Объект");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", ТекстСОтступом(УсловиеОтбора, "	"));
	
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
КонецПроцедуры

// Для процедуры СобратьЧастиЗапросовЗаполнения.
Функция ТекстЗапросаКлючейДоступаДляОбновленияПрав(Контекст)
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 995
	|	КлючиДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.КлючиДоступа КАК КлючиДоступа
	|ГДЕ
	|	КлючиДоступа.Список = &Список
	|	И КлючиДоступа.СоставПолей = &СоставПолей
	|	И КлючиДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И КлючиДоступа.Ссылка > &ПоследнийКлючДоступа
	|	И &УточнениеПланаЗапроса
	|
	|УПОРЯДОЧИТЬ ПО
	|	КлючиДоступа.Ссылка";
	
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Возврат ТекстЗапроса;
	
КонецФункции

// Для процедуры СобратьЧастиЗапросовЗаполнения.
Функция ТекстЗапросаКлючейПоВедущимКлючамДляОбновленияПрав(Контекст)
	
	Если Контекст.ЧастиУсловияОтбораПоВедущимКлючамДоступа.Количество() = 0 Тогда
		Возврат "";
	КонецЕсли;
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 995
	|	КлючиДоступа.Ссылка КАК Ссылка
	|ИЗ
	|	Справочник.КлючиДоступа КАК КлючиДоступа
	|ГДЕ
	|	КлючиДоступа.Список = &Список
	|	И КлючиДоступа.СоставПолей = &СоставПолей
	|	И КлючиДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И &УсловиеОтбора
	|	И &УточнениеПланаЗапроса
	|
	|УПОРЯДОЧИТЬ ПО
	|	КлючиДоступа.Ссылка";
	
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Если Контекст.ЧастиУсловияОтбораПоВедущимКлючамДоступа.Количество() = 1 Тогда
		УсловиеОтбора = Контекст.ЧастиУсловияОтбораПоВедущимКлючамДоступа[0];
	Иначе
		УсловиеОтбора = "(" + СтрСоединить(Контекст.ЧастиУсловияОтбораПоВедущимКлючамДоступа,
			Символы.ПС + "			ИЛИ ") + ")"; // @query-part-1
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", УсловиеОтбора);
	
	Возврат ТекстЗапроса;
	
КонецФункции

// Для процедуры СобратьЧастиЗапросовЗаполнения.
Функция ТекстЗапросаУстаревшихКлючейДоступа(Контекст)
	
	Если Контекст.ЭтоСсылочныйТип Тогда
		ТекстЗапросаИспользуемых = // @query-part-1
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	КлючиДоступаКОбъектам.#КлючДоступаПользователей КАК КлючДоступа
		|ПОМЕСТИТЬ ИспользуемыеКлючиДоступа
		|ИЗ
		|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
		|ГДЕ
		|	ТИПЗНАЧЕНИЯ(КлючиДоступаКОбъектам.Объект) = ТИП(&ТекущийСписок)
		|	И КлючиДоступаКОбъектам.#КлючДоступаПользователей > &ПоследнийКлючДоступа
		|	И &УточнениеПланаЗапроса
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	КлючДоступа"; // @query-part
	Иначе
		ТекстЗапросаИспользуемых =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	КлючиДоступаКРегистрам.КлючДоступа КАК КлючДоступа
		|ПОМЕСТИТЬ ИспользуемыеКлючиДоступа
		|ИЗ
		|	&ТекущийСписок КАК КлючиДоступаКРегистрам
		|ГДЕ
		|	КлючиДоступаКРегистрам.Регистр = &Список
		|	И КлючиДоступаКРегистрам.ВариантДоступа = &ВариантДоступа
		|	И КлючиДоступаКРегистрам.КлючДоступа > &ПоследнийКлючДоступа
		|	И &УточнениеПланаЗапроса
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	КлючДоступа";
		Если ЗначениеЗаполнено(Контекст.ИмяОтдельногоРегистраКлючей) Тогда
			ТекстЗапросаИспользуемых = СтрЗаменить(ТекстЗапросаИспользуемых,
				"КлючиДоступаКРегистрам.Регистр = &Список
				|	И ", ""); // @query-part-1
		КонецЕсли;
	КонецЕсли;
	
	ТекстЗапроса =
	"ВЫБРАТЬ ПЕРВЫЕ 995
	|	КлючиДоступа.Ссылка КАК Ссылка,
	|	КлючиДоступа.Список КАК Список,
	|	КлючиДоступа.СоставПолей КАК СоставПолей,
	|	КлючиДоступа.Хеш КАК Хеш,
	|	НЕ ИспользуемыеКлючиДоступа.КлючДоступа ЕСТЬ NULL КАК Используется,
	|	КлючиДоступа.НеИспользуетсяС <> ДАТАВРЕМЯ(1, 1, 1)
	|		И КлючиДоступа.НеИспользуетсяС < &ДатаУстаревания КАК Удалить
	|ИЗ
	|	Справочник.КлючиДоступа КАК КлючиДоступа
	|		ЛЕВОЕ СОЕДИНЕНИЕ ИспользуемыеКлючиДоступа КАК ИспользуемыеКлючиДоступа
	|		ПО (ИспользуемыеКлючиДоступа.КлючДоступа = КлючиДоступа.Ссылка)
	|ГДЕ
	|	КлючиДоступа.Список = &Список
	|	И КлючиДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И КлючиДоступа.Ссылка > &ПоследнийКлючДоступа
	|	И ВЫБОР
	|			КОГДА КлючиДоступа.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
	|				ТОГДА ИспользуемыеКлючиДоступа.КлючДоступа ЕСТЬ NULL
	|			ИНАЧЕ НЕ ИспользуемыеКлючиДоступа.КлючДоступа ЕСТЬ NULL
	|					ИЛИ КлючиДоступа.НеИспользуетсяС < &ДатаУстаревания
	|		КОНЕЦ
	|	И &УточнениеПланаЗапроса
	|
	|УПОРЯДОЧИТЬ ПО
	|	КлючиДоступа.Ссылка";
	
	ТекстЗапроса = ТекстЗапросаИспользуемых
		+ ОбщегоНазначения.РазделительПакетаЗапросов() + ТекстЗапроса;
	
	ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст);
	Возврат ТекстЗапроса;
	
КонецФункции

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ЗаполнитьЗапросыПроверкиПравЧтениеИзменение(Результат, Контекст)
	
	Если Контекст.ИмяКоллекцииТипа = "ЖурналыДокументов" Тогда
		Возврат;
	КонецЕсли;
	
	Свойства = СвойстваОграниченияСписка(Контекст.Список, Контекст);
	
	Если Контекст.ЭтоСсылочныйТип
	 Или Результат.ИспользуетсяОграничениеПоВладельцу Тогда
		
		Если Свойства.ОграничениеВШаблонахЧерезКлючиДоступаПользователейИГруппДоступа
		 Или Свойства.ОграничениеВШаблонахЧерезКлючиДоступаПользователей Тогда
			Если Контекст.ДляВнешнихПользователей Тогда
				ТекстЗапроса =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА КАК ЗначениеИстина
				|ИЗ
				|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
				|ГДЕ
				|	КлючиДоступаКОбъектам.Объект = &Объект
				|	И (ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаВнешнихПользователей КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКОбъектам.КлючДоступаВнешнихПользователей
				|					И РазрешенныеКлючиДоступа.ВнешнийПользователь В (&РазрешенныйПользователь, &РазрешенныйНаборГруппПользователей)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение)
				|			ИЛИ ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКОбъектам.КлючДоступаВнешнихПользователей
				|					И РазрешенныеКлючиДоступа.НаборГруппДоступа В (&РазрешенныйНаборГруппДоступа, &РазрешенныйПустойНаборГруппДоступа)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение))";
			Иначе
				ТекстЗапроса =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА КАК ЗначениеИстина
				|ИЗ
				|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
				|ГДЕ
				|	КлючиДоступаКОбъектам.Объект = &Объект
				|	И (ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаПользователей КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКОбъектам.КлючДоступаПользователей
				|					И РазрешенныеКлючиДоступа.Пользователь В (&РазрешенныйПользователь, &РазрешенныйНаборГруппПользователей)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение)
				|			ИЛИ ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКОбъектам.КлючДоступаПользователей
				|					И РазрешенныеКлючиДоступа.НаборГруппДоступа В (&РазрешенныйНаборГруппДоступа, &РазрешенныйПустойНаборГруппДоступа)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение))";
			КонецЕсли;
		Иначе
			Если Контекст.ДляВнешнихПользователей Тогда
				ТекстЗапроса =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА КАК ЗначениеИстина
				|ИЗ
				|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
				|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК РазрешенныеКлючиДоступа
				|		ПО (КлючиДоступаКОбъектам.Объект = &Объект)
				|			И (РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКОбъектам.КлючДоступаВнешнихПользователей)
				|			И (РазрешенныеКлючиДоступа.НаборГруппДоступа В (&РазрешенныйНаборГруппДоступа, &РазрешенныйПустойНаборГруппДоступа))
				|			И (РазрешенныеКлючиДоступа.ПравоИзменение)";
			Иначе
				ТекстЗапроса =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА КАК ЗначениеИстина
				|ИЗ
				|	РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам
				|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК РазрешенныеКлючиДоступа
				|		ПО (КлючиДоступаКОбъектам.Объект = &Объект)
				|			И (РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКОбъектам.КлючДоступаПользователей)
				|			И (РазрешенныеКлючиДоступа.НаборГруппДоступа В (&РазрешенныйНаборГруппДоступа, &РазрешенныйПустойНаборГруппДоступа))
				|			И (РазрешенныеКлючиДоступа.ПравоИзменение)";
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если Не Результат.ИспользуетсяОграничениеПоВладельцу Тогда
		ТекстЗапросаСПроверкойПоВладельцамНастроекПрав = "";
	Иначе
		ТипКонечногоПоля = Контекст.СвойстваПолей[0].ТипКонечногоПоля;
		ТипыВладельцевНастроекПравПоля = Новый Соответствие;
		Если Контекст.ОграничениеДоступаВключено Тогда
			Для Каждого КлючИЗначение Из Контекст.ТипыВладельцевНастроекПрав Цикл
				Если ТипКонечногоПоля.СодержитТип(КлючИЗначение.Ключ) Тогда
					ТипыВладельцевНастроекПравПоля.Вставить(КлючИЗначение.Ключ,
						ИмяТипаНаЯзыкеЗапросов(КлючИЗначение.Ключ));
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Если ТипыВладельцевНастроекПравПоля.Количество() = 0 Тогда
			ТекстЗапросаСПроверкойПоВладельцамНастроекПрав = "";
			
		ИначеЕсли ТипКонечногоПоля.Типы().Количество() = ТипыВладельцевНастроекПравПоля.Количество() Тогда
			ТекстЗапросаСПроверкойПоВладельцамНастроекПрав =
				ТекстЗапросаСПроверкойПоВладельцамНастроекПрав();
		Иначе
			Если Контекст.ЭтоСсылочныйТип Тогда
				ТекстЗапросаСПроверкойПоВладельцамНастроекПрав =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА КАК ЗначениеИстина
				|ГДЕ
				|	ВЫБОР
				|			КОГДА &УсловиеПроверкиТипаВладельцаНастроекПрав
				|				ТОГДА &УсловиеСПроверкойПоВладельцамНастроекПрав
				|			ИНАЧЕ ИСТИНА В
				|				(&ТекстЗапросаСтандартнойПроверки)
				|		КОНЕЦ";
			Иначе
				ТекстЗапросаСПроверкойПоВладельцамНастроекПрав =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ЛОЖЬ КАК ЗначениеЛожь
				|ИЗ
				|	&ТекущаяТаблица КАК ТекущаяТаблица
				|ГДЕ
				|	&ОтборПоИзмерениям
				|	И ВЫБОР
				|			КОГДА &УсловиеПроверкиТипаВладельцаНастроекПрав
				|				ТОГДА &УсловиеСПроверкойПоВладельцамНастроекПрав
				|			ИНАЧЕ ИСТИНА В
				|				(&ТекстЗапросаСтандартнойПроверки)
				|		КОНЕЦ";
				ТекстЗапросаСПроверкойПоВладельцамНастроекПрав = СтрЗаменить(ТекстЗапросаСПроверкойПоВладельцамНастроекПрав,
					"&ТекущаяТаблица", Контекст.Список)
			КонецЕсли;
			УсловиеПроверкиТипа = "";
			Для Каждого СвойстваТипа Из ТипыВладельцевНастроекПравПоля Цикл
				УсловиеПроверкиТипа = УсловиеПроверкиТипа + ?(УсловиеПроверкиТипа = "", "", "
				|					") + "ТИПЗНАЧЕНИЯ(&Объект) = ТИП(" + СвойстваТипа.Значение + ")"; // @query-part-2
			КонецЦикла;
			ТекстЗапросаСПроверкойПоВладельцамНастроекПрав = СтрЗаменить(
				ТекстЗапросаСПроверкойПоВладельцамНастроекПрав,
				"&УсловиеПроверкиТипаВладельцаНастроекПрав",
				УсловиеПроверкиТипа);
			
			ТекстЗапросаСПроверкойПоВладельцамНастроекПрав = СтрЗаменить(
				ТекстЗапросаСПроверкойПоВладельцамНастроекПрав,
				"&УсловиеСПроверкойПоВладельцамНастроекПрав",
				ТекстСОтступом(УсловиеИзЗапросаСПроверкойПоВладельцамНастроекПрав(),
					"				"));
			
			ТекстЗапросаСПроверкойПоВладельцамНастроекПрав = СтрЗаменить(
				ТекстЗапросаСПроверкойПоВладельцамНастроекПрав,
				"&ТекстЗапросаСтандартнойПроверки",
				ТекстСОтступом(ТекстЗапроса, "				"));
		КонецЕсли;
	КонецЕсли;
	
	Если Результат.ИспользуетсяОграничениеПоВладельцу Тогда
		Если Контекст.ЭтоСсылочныйТип Тогда
			ПолеОбъекта = "ВЫРАЗИТЬ(&Объект КАК " + Контекст.Список + ")." + Результат.ПолеВладельца.Имя; // @query-part-1
			Результат.ПолеОбъектаВладельцаВЗапросеПроверкиПрав = ПолеОбъекта;
		Иначе
			ПолеОбъекта = "ТекущаяТаблица." + Результат.ПолеВладельца.Имя;
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Объект", ПолеОбъекта);
		ТекстЗапросаСПроверкойПоВладельцамНастроекПрав =
			СтрЗаменить(ТекстЗапросаСПроверкойПоВладельцамНастроекПрав, "&Объект", ПолеОбъекта);
		
	ИначеЕсли Не Контекст.ЭтоСсылочныйТип Тогда
		
		Если Свойства.ОграничениеВШаблонахЧерезКлючиДоступаПользователейИГруппДоступа
		 Или Свойства.ОграничениеВШаблонахЧерезКлючиДоступаПользователей Тогда
			
			Если Контекст.ДляВнешнихПользователей Тогда
				ТекстЗапроса =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА КАК ЗначениеИстина
				|ИЗ
				|	РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
				|ГДЕ
				|	КлючиДоступаКРегистрам.Регистр = ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка)
				|	И КлючиДоступаКРегистрам.Поле1 = &ТекущаяТаблицаПоле1
				|	И (ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаВнешнихПользователей КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКРегистрам.КлючДоступа
				|					И РазрешенныеКлючиДоступа.ВнешнийПользователь В (&РазрешенныйПользователь, &РазрешенныйНаборГруппПользователей)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение)
				|			ИЛИ ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКРегистрам.КлючДоступа
				|					И РазрешенныеКлючиДоступа.НаборГруппДоступа В (&РазрешенныйНаборГруппДоступа, &РазрешенныйПустойНаборГруппДоступа)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение))";
			Иначе
				ТекстЗапроса =
				"ВЫБРАТЬ ПЕРВЫЕ 1
				|	ИСТИНА КАК ЗначениеИстина
				|ИЗ
				|	РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
				|ГДЕ
				|	КлючиДоступаКРегистрам.Регистр = ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка)
				|	И КлючиДоступаКРегистрам.Поле1 = &ТекущаяТаблицаПоле1
				|	И (ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаПользователей КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКРегистрам.КлючДоступа
				|					И РазрешенныеКлючиДоступа.Пользователь В (&РазрешенныйПользователь, &РазрешенныйНаборГруппПользователей)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение)
				|			ИЛИ ИСТИНА В
				|				(ВЫБРАТЬ ПЕРВЫЕ 1
				|					ИСТИНА
				|				ИЗ
				|					РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК РазрешенныеКлючиДоступа
				|				ГДЕ
				|					РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКРегистрам.КлючДоступа
				|					И РазрешенныеКлючиДоступа.НаборГруппДоступа В (&РазрешенныйНаборГруппДоступа, &РазрешенныйПустойНаборГруппДоступа)
				|					И РазрешенныеКлючиДоступа.ПравоИзменение))";
			КонецЕсли;
		Иначе
			ТекстЗапроса =
			"ВЫБРАТЬ ПЕРВЫЕ 1
			|	ИСТИНА КАК ЗначениеИстина
			|ИЗ
			|	РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
			|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаНаборовГруппДоступа КАК РазрешенныеКлючиДоступа
			|		ПО (КлючиДоступаКРегистрам.Регистр = ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка))
			|			И (КлючиДоступаКРегистрам.Поле1 = &ТекущаяТаблицаПоле1)
			|			И (РазрешенныеКлючиДоступа.КлючДоступа = КлючиДоступаКРегистрам.КлючДоступа)
			|			И (РазрешенныеКлючиДоступа.НаборГруппДоступа В (&РазрешенныйНаборГруппДоступа, &РазрешенныйПустойНаборГруппДоступа))
			|			И (РазрешенныеКлючиДоступа.ПравоИзменение)";
		КонецЕсли;
		
		УточнитьРегистрКлючейИУсловиеСоединения(ТекстЗапроса, Результат, Контекст);
		
		ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейСуществующихЗаписей =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	&ТекущаяТаблицаПоле1 КАК Поле1
		|ИЗ
		|	&Список КАК ТекущаяТаблица
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
		|		ПО (КлючиДоступаКРегистрам.Регистр = ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка))
		|			И (КлючиДоступаКРегистрам.Поле1 = &ТекущаяТаблицаПоле1)
		|ГДЕ
		|	&ОтборПоИзмерениям
		|	И КлючиДоступаКРегистрам.ВариантДоступа ЕСТЬ NULL";
		ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейНовыхЗаписей =
		"ВЫБРАТЬ
		|	&ТекущаяТаблицаПоле1 КАК ТекущаяТаблицаПоле1
		|ПОМЕСТИТЬ КомбинацииЗначенийОпорныхПолей
		|ИЗ
		|	&КомбинацииЗначенийОпорныхПолей КАК ТекущаяТаблица
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	&ТекущаяТаблицаПоле1 КАК Поле1
		|ИЗ
		|	КомбинацииЗначенийОпорныхПолей КАК ТекущаяТаблица
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКРегистрам КАК КлючиДоступаКРегистрам
		|		ПО (КлючиДоступаКРегистрам.Регистр = ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка))
		|			И (КлючиДоступаКРегистрам.Поле1 = &ТекущаяТаблицаПоле1)
		|ГДЕ
		|	КлючиДоступаКРегистрам.ВариантДоступа ЕСТЬ NULL";
		УточнитьРегистрКлючейИУсловиеСоединения(ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейСуществующихЗаписей,
			Результат, Контекст, Истина);
		УточнитьРегистрКлючейИУсловиеСоединения(ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейНовыхЗаписей,
			Результат, Контекст, Истина);
		
		Результат.ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейСуществующихЗаписей =
			ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейСуществующихЗаписей;
		Результат.ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейНовыхЗаписей =
			ТекстЗапросаНовыхКомбинацийЗначенийОпорныхПолейНовыхЗаписей;
	КонецЕсли;
	
	Если Не Контекст.ЭтоСсылочныйТип Тогда
		ТекстЗапросаРегистра =
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ЛОЖЬ КАК ЗначениеЛожь
		|ИЗ
		|	&ТекущаяТаблица КАК ТекущаяТаблица
		|ГДЕ
		|	&ОтборПоИзмерениям
		|	И НЕ ИСТИНА В
		|				(&УсловиеЗапроса)";
		ТекстЗапросаРегистра = СтрЗаменить(ТекстЗапросаРегистра, "&ТекущаяТаблица", Контекст.Список);
		ТекстЗапроса = СтрЗаменить(ТекстЗапросаРегистра, "&УсловиеЗапроса", ТекстСОтступом(ТекстЗапроса, "				"));
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ТекстЗапросаСПроверкойПоВладельцамНастроекПрав) Тогда
		Результат.ТекстЗапросаПроверкиПравЧтениеИзменение = ТекстЗапросаСПроверкойПоВладельцамНастроекПрав;
	Иначе
		Результат.ТекстЗапросаПроверкиПравЧтениеИзменение = ТекстЗапроса;
	КонецЕсли;
	
	Если Не Результат.ОграничениеЧтенияОтключено Тогда
		Результат.ТекстЗапросаПроверкиПраваЧтение = СтрЗаменить(ТекстЗапроса,
			"РазрешенныеКлючиДоступа.ПравоИзменение", "Истина");
	КонецЕсли;
	
	Если Результат.ИспользуетсяОграничениеПоВладельцу
	   И Результат.ПолеВладельца.ИзменениеКакЧтение Тогда
		
		Результат.ТекстЗапросаПроверкиПравЧтениеИзменение = Результат.ТекстЗапросаПроверкиПраваЧтение;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ЗаполнитьЗапросыПроверкиПравЧтениеИзменение.
Функция УсловиеИзЗапросаСПроверкойПоВладельцамНастроекПрав()
	
	Строки = СтрРазделить(ТекстЗапросаСПроверкойПоВладельцамНастроекПрав(), Символы.ПС, "");
	Строки.Удалить(0);
	Строки.Удалить(0);
	Строки.Удалить(0);
	
	Возврат СокрЛП(СтрСоединить(Строки, Символы.ПС));
	
КонецФункции

// Для процедуры ЗаполнитьЗапросыПроверкиПравЧтениеИзменение.
Функция ТекстЗапросаСПроверкойПоВладельцамНастроекПрав()
	
	Возврат
	"ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА КАК ЗначениеИстина
	|ГДЕ
	|	ИСТИНА В
	|			(ВЫБРАТЬ ПЕРВЫЕ 1
	|				ИСТИНА
	|			ИЗ
	|				РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|					ПО
	|						НаследованиеНастроек.Объект = &Объект
	|							И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|							И НастройкиПрав.Таблица = &ИдентификаторТаблицыНастроекПрав
	|							И НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньРазрешенияИзменения
	|					ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|					ПО
	|						СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь
	|							И СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)
	|	И НЕ ЛОЖЬ В
	|				(ВЫБРАТЬ ПЕРВЫЕ 1
	|					ЛОЖЬ
	|				ИЗ
	|					РегистрСведений.НастройкиПравОбъектов КАК НастройкиПрав
	|						ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.НаследованиеНастроекПравОбъектов КАК НаследованиеНастроек
	|						ПО
	|							НаследованиеНастроек.Объект = &Объект
	|								И НастройкиПрав.Объект = НаследованиеНастроек.Родитель
	|								И НастройкиПрав.Таблица = &ИдентификаторТаблицыНастроекПрав
	|								И НаследованиеНастроек.УровеньИспользования < НастройкиПрав.УровеньЗапрещенияИзменения
	|						ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
	|						ПО
	|							СоставыГруппПользователей.Пользователь = &АвторизованныйПользователь
	|								И СоставыГруппПользователей.ГруппаПользователей = НастройкиПрав.Пользователь)";
	
КонецФункции

// Для процедуры ЗаполнитьЗапросыПроверкиПравЧтениеИзменение.
Процедура УточнитьРегистрКлючейИУсловиеСоединения(ТекстЗапроса, Результат, Контекст, ДобавитьПоляВыбора = Ложь)
	
	Если ЗначениеЗаполнено(Результат.ИмяОтдельногоРегистраКлючей) Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
			"РегистрСведений.КлючиДоступаКРегистрам",
			"РегистрСведений." + Результат.ИмяОтдельногоРегистраКлючей);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
			"КлючиДоступаКРегистрам.Регистр = ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка)", // @query-part-1
			"ИСТИНА");
	Иначе
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
			"ИдентификаторыОбъектовМетаданных.ПустаяСсылка",
			УправлениеДоступомСлужебныйПовтИсп.ОписаниеПредопределенногоИдентификатораОбъектаМетаданных(
				Контекст.Список));
	КонецЕсли;
	
	ИспользуемыеВариантыДоступа = Контекст.ОсновныеВариантыДоступа.Получить(Контекст.Список);
	Если ИспользуемыеВариантыДоступа = Неопределено Тогда
		ИспользуемыйВариантДоступа = НовыйИспользуемыйВариантДоступа();
		ИспользуемыйВариантДоступа.ВариантДоступа = Результат.ВариантДоступа;
		ИспользуемыйВариантДоступа.ПоляСоединения = СтрСоединить(Результат.ОпорныеПоля.Используемые, ",");
		ИспользуемыеВариантыДоступа = Новый Массив;
		ИспользуемыеВариантыДоступа.Добавить(ИспользуемыйВариантДоступа);
	КонецЕсли;
	Если ДобавитьПоляВыбора Тогда
		ИспользуемыйВариантДоступа = ИспользуемыеВариантыДоступа[0];
		ИспользуемыеВариантыДоступа = Новый Массив;
		ИспользуемыеВариантыДоступа.Добавить(ИспользуемыйВариантДоступа);
	КонецЕсли;
	
	УсловияОтбора = Новый Массив;
	УсловияСоединения = Новый Массив;
	Для Каждого ИспользуемыйВариантДоступа Из ИспользуемыеВариантыДоступа Цикл
		Условие = Новый Массив;
		ВариантДоступаСтрокой = XMLСтрока(ИспользуемыйВариантДоступа.ВариантДоступа);
		ВариантДоступаСтрокой = ?(ЗначениеЗаполнено(ВариантДоступаСтрокой), ВариантДоступаСтрокой, "NULL");
		Условие.Добавить("КлючиДоступаКРегистрам.ВариантДоступа = " + ВариантДоступаСтрокой);
		НомерПоля = 1;
		ИменаПолей = СтрРазделить(ИспользуемыйВариантДоступа.ПоляСоединения, ",", Ложь);
		Для Каждого ИмяПоля Из ИменаПолей Цикл
			Условие.Добавить("КлючиДоступаКРегистрам.Поле" + НомерПоля + " = ТекущаяТаблица." + ИмяПоля);
			НомерПоля = НомерПоля + 1;
		КонецЦикла;
		Если ИспользуемыеВариантыДоступа.Количество() = 1 Тогда
			УсловияОтбора.Добавить("И " + СтрСоединить(Условие, Символы.ПС + "И ")); // @query-part-1 @query-part-2
			УсловияСоединения.Добавить("И (" + СтрСоединить(Условие, ")" + Символы.ПС + "И (") + ")"); // @query-part-1 @query-part-3
		Иначе
			УсловияОтбора.Добавить(СтрСоединить(Условие, Символы.ПС + "			И "));
			УсловияСоединения.Добавить(СтрСоединить(Условие, Символы.ПС + "		И "));
		КонецЕсли;
	КонецЦикла;
	
	Если ИспользуемыеВариантыДоступа.Количество() = 1 Тогда
		УсловиеОтбора = УсловияОтбора[0];
		УсловиеСоединения = УсловияСоединения[0];
	Иначе
		УсловиеОтбора = "И (" + СтрСоединить(УсловияОтбора, Символы.ПС + "		ИЛИ ") + ")"; // @query-part-1 @query-part-2
		УсловиеСоединения =  "И (" + СтрСоединить(УсловияСоединения, Символы.ПС + "	ИЛИ ") + ")"; // @query-part-1 @query-part-2
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
		"И КлючиДоступаКРегистрам.Поле1 = &ТекущаяТаблицаПоле1", // @query-part-1
		ТекстСОтступом(СокрЛ(УсловиеОтбора), "	"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
		"И (КлючиДоступаКРегистрам.Поле1 = &ТекущаяТаблицаПоле1)", // @query-part-1
		ТекстСОтступом(СокрЛ(УсловиеСоединения), "			"));
	
	Если Не ДобавитьПоляВыбора Тогда
		Возврат;
	КонецЕсли;
	
	ОпорныеПоля = "";
	ПоляВыбора = "";
	НомерПоля = 1;
	Для Каждого Поле Из Результат.ОпорныеПоля.Используемые Цикл
		ОпорныеПоля = ОпорныеПоля + ?(ОпорныеПоля = "", "", "," + Символы.ПС)
			+ "ТекущаяТаблица." + Поле + " КАК " + Поле; // @query-part-2
		ПоляВыбора = ПоляВыбора + ?(ПоляВыбора = "", "", "," + Символы.ПС)
			+ "ТекущаяТаблица." + Поле + " КАК Поле" + НомерПоля; // @query-part-2
		НомерПоля = НомерПоля + 1;
	КонецЦикла;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
		"&ТекущаяТаблицаПоле1 КАК ТекущаяТаблицаПоле1", // @query-part-1
		ТекстСОтступом(СокрЛ(ОпорныеПоля), "	"));
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса,
		"&ТекущаяТаблицаПоле1 КАК Поле1", // @query-part-1
		ТекстСОтступом(СокрЛ(ПоляВыбора), "	"));
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Список", Контекст.Список);
	
КонецПроцедуры

// Для функции СобратьЧастиЗапросов.
Процедура ПодставитьОбщиеПараметрыВЗапрос(ТекстЗапроса, Контекст)
	
	Если Контекст.ДляВнешнихПользователей Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#КлючДоступаПользователейКОбъекту", "КлючДоступаВнешнихПользователей");
	Иначе
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#КлючДоступаПользователейКОбъекту", "КлючДоступаПользователей");
	КонецЕсли;
	
	Если Контекст.ЭтоСсылочныйТип Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийСписок", Контекст.Список);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#КлючиДоступаКДанным", "КлючиДоступаКОбъектам");
		
		Если Контекст.ДляВнешнихПользователей Тогда
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#КлючДоступаПользователей", "КлючДоступаВнешнихПользователей");
		Иначе
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#КлючДоступаПользователей", "КлючДоступаПользователей");
		КонецЕсли;
	Иначе
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийРегистр", Контекст.Список);
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#КлючиДоступаКДанным", "ТекущийСписок");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТекущийСписок.Регистр = &ИдентификаторРегистра", "#1");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТекущийСписок.ВариантДоступа = &ВариантДоступа", "#2");
		Номер = 0;
		Для Каждого ИмяОпорногоПоля Из Контекст.ОпорныеПоля.Используемые Цикл
			Номер = Номер + 1;
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТекущийСписок."
				+ ИмяОпорногоПоля, "ТекущийСписок.Поле" + Номер);
			
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТекущийСписокИсточник."
				+ ИмяОпорногоПоля, "ТекущийСписокИсточник.Поле" + Номер);
		КонецЦикла;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#1", "ТекущийСписок.Регистр = &ИдентификаторРегистра");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#2", "ТекущийСписок.ВариантДоступа = &ВариантДоступа");
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущийСписок", ?(Контекст.ИмяОтдельногоРегистраКлючей = "",
			"РегистрСведений.КлючиДоступаКРегистрам", "РегистрСведений." + Контекст.ИмяОтдельногоРегистраКлючей));
		
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#КлючДоступаПользователей", "КлючДоступа");
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&СоставПолей", Формат(Контекст.СоставПолей, "ЧН=0; ЧГ="));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ВариантДоступа", XMLСтрока(Контекст.ВариантДоступа));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ДляВнешнихПользователей",
		?(Контекст.ДляВнешнихПользователей, "ИСТИНА", "ЛОЖЬ"));
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ДобавитьПроверкуШапкиКлюча(Контекст, НомерШапки)
	
	ГруппаПолей = Контекст.ГруппыПолей.Получить("Шапка" + НомерШапки);
	Если ГруппаПолей = Неопределено Тогда
		Если НомерШапки > 0 Тогда
			Возврат;
		КонецЕсли;
		СоединенияИПоля = Новый Структура("Соединения, Поля", "", "");
	Иначе
		СоединенияИПоля = СоединенияИПоляПоТаблицам(ГруппаПолей,
			Ложь, НомерШапки).Получить("ТекущийСписок");
	КонецЕсли;
	
	Если НомерШапки = 0 Тогда
		Соединения = СоединенияИПоля.Соединения + "
		|ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КлючиДоступа КАК Шапка0
		|ПО (Шапка0.Ссылка = #КлючиДоступаКДанным.#КлючДоступаПользователей)" // @query-part-1 
		+ ?(Контекст.СоставПолей = 0, "
		|	И (Шапка0.Список = &Список)", "") // @query-part-1 
		+ "
		|	И (Шапка0.СоставПолей = &СоставПолей)"; // @query-part-1
	Иначе
		Соединения = СоединенияИПоля.Соединения + "
		|ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КлючиДоступа.Шапка КАК Шапка?
		|ПО (Шапка?.Ссылка = #КлючиДоступаКДанным.#КлючДоступаПользователей)
		|	И (Шапка?.НомерСтроки = &НомерШапки)"; // @query-part-1
		Соединения = СтрЗаменить(Соединения, "&НомерШапки", НомерШапки);
	КонецЕсли;
	
	Соединения = СокрЛ(Соединения) + ТекстСОтступом(СоединенияИПоля.Поля, "	");
	Соединения = ТекстСОтступом(Соединения, "		");
	
	ШаблонТекстаЧастиУсловия =
	"НЕ ИСТИНА В
	|(ВЫБРАТЬ ПЕРВЫЕ 1
	|	ИСТИНА
	|ИЗ
	|	(ВЫБРАТЬ
	|		ИСТИНА КАК ЗначениеИстина) КАК ЗначениеИстина
	|		#Соединения
	|ГДЕ
	|	&УсловиеЗапроса)"; // @query-part-1
	
	ТекстЧастиУсловия = ШаблонТекстаЧастиУсловия;
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "#Соединения", Соединения);
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "&УсловиеЗапроса", 
		ТекстСОтступом("НЕ Шапка?.Ссылка ЕСТЬ NULL", "	")); // @query-part-1
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "Шапка?", "Шапка" + НомерШапки);
	Контекст.ЧастиУсловияПроверки.Добавить(ТекстЧастиУсловия);
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ДобавитьПроверкуТабличнойЧастиКлюча(Контекст, НомерТабличнойЧастиКлюча)
	
	ТаблицыПоГруппам  = Контекст.ГруппыДополнительныхТаблиц.ТаблицыПоГруппам;
	ГруппаДополнительныхТаблиц = ТаблицыПоГруппам.Получить(НомерТабличнойЧастиКлюча
		- (Контекст.КоличествоТабличныхЧастейКлюча - ТаблицыПоГруппам.Количество()));
	
	ИмяТабличнойЧастиКлюча = "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча;
	ГруппаПолей = Контекст.ГруппыПолей.Получить(ИмяТабличнойЧастиКлюча);
	
	СоединенияИПоляПоТаблицам = СоединенияИПоляПоТаблицам(ГруппаПолей,
		Истина, , , ВГруппеОднаДополнительнаяТаблицаСПолями(Контекст, ГруппаДополнительныхТаблиц));
	
	Соединения = "";
	Условие = "";
	Поля = "";
	
	Если ГруппаДополнительныхТаблиц = Неопределено Тогда
		ПсевдонимТабличнойЧастиОбъекта = Контекст.ПсевдонимыТабличныхЧастейОбъекта.Получить(НомерТабличнойЧастиКлюча);
		Если ПсевдонимТабличнойЧастиОбъекта = Неопределено Тогда
			СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить("ТекущийСписок");
			Условие = "ИСТИНА";
		Иначе
			ИмяТабличнойЧастиОбъекта = СтрЗаменить(ПсевдонимТабличнойЧастиОбъекта, "ТекущийСписок", "");
			СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить(ПсевдонимТабличнойЧастиОбъекта);
			// @query-part-1 @query-part-2
			Соединения = Соединения + "
			|ЛЕВОЕ СОЕДИНЕНИЕ &ТекущийСписок." + ИмяТабличнойЧастиОбъекта + " КАК " + ПсевдонимТабличнойЧастиОбъекта + "
			|ПО " + ПсевдонимТабличнойЧастиОбъекта + ".Ссылка = ТекущийСписок.Ссылка"; // @query-part-1
			Условие = ПсевдонимТабличнойЧастиОбъекта + ".Ссылка #ПроверкаNULL";
		КонецЕсли;
		Соединения = Соединения + СоединенияИПоля.Соединения;
		Поля = СоединенияИПоля.Поля;
	Иначе
		Для Каждого ДополнительнаяТаблица Из ГруппаДополнительныхТаблиц Цикл
			// @query-part-1 @query-part-2
			Соединения = Соединения + "
			|ЛЕВОЕ СОЕДИНЕНИЕ " + ДополнительнаяТаблица.Таблица + " КАК " + ДополнительнаяТаблица.Псевдоним + "
			|ПО " + ТекстСОтступом(ДополнительнаяТаблица.ТекстУсловияСоединения, "	"); // @query-part-1
			СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить(ДополнительнаяТаблица.Псевдоним);
			Если СоединенияИПоля = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Соединения = Соединения + СоединенияИПоля.Соединения;
			Условие = Условие + ?(Условие = "", "", "
			|ИЛИ ") + ДополнительнаяТаблица.ПолеПроверкиСоединения + " #ПроверкаNULL"; // @query-part-1
			Поля = Поля + СоединенияИПоля.Поля;
		КонецЦикла;
		Если СтрЧислоСтрок(Условие) > 1 Тогда
			Условие = "(" + Условие + ")";
		КонецЕсли;
	КонецЕсли;
	ИсходныеСоединения = Соединения;
	ИсходноеУсловие = Условие;
	
	ШаблонТекстаЧастиУсловия =
	"ЛОЖЬ В
	|(ВЫБРАТЬ ПЕРВЫЕ 1
	|	ЛОЖЬ
	|ИЗ
	|	(ВЫБРАТЬ
	|		ИСТИНА КАК ЗначениеИстина) КАК ЗначениеИстина
	|		#Соединения
	|ГДЕ
	|	&УсловиеЗапроса)"; // @query-part
	
	// Прямое соединение (проверка наличия требуемых записей в ключе).
	Соединения = ИсходныеСоединения + "
	|ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КлючиДоступа.ТабличнаяЧасть? КАК ТабличнаяЧасть?
	|ПО (ТабличнаяЧасть?.Ссылка = #КлючиДоступаКДанным.#КлючДоступаПользователей)"; // @query-part
	Соединения = СокрЛ(Соединения) + ТекстСОтступом(Поля, "	");
	Соединения = ТекстСОтступом(Соединения, "		");
	
	Условие =
	"ТабличнаяЧасть?.Ссылка ЕСТЬ NULL
	|И " + ТекстСОтступом(СокрЛ(ИсходноеУсловие), "	"); // @query-part-1
	Условие = ТекстСОтступом(Условие, "	");
	Условие = СтрЗаменить(Условие, "#ПроверкаNULL", "ЕСТЬ НЕ NULL"); // @query-part-2
	
	ТекстЧастиУсловия = ШаблонТекстаЧастиУсловия;
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "#Соединения",     Соединения);
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "&УсловиеЗапроса", Условие);
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "ТабличнаяЧасть?", ИмяТабличнойЧастиКлюча);
	Контекст.ЧастиУсловияПроверки.Добавить(ТекстЧастиУсловия);
	
	// Обратное соединение (проверка отсутствия лишних записей в ключе).
	Если ГруппаДополнительныхТаблиц = Неопределено
	 Или ГруппаДополнительныхТаблиц.Количество() = 1 Тогда
		
		Соединения =
		"ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КлючиДоступа.ТабличнаяЧасть? КАК ТабличнаяЧасть?
		|ПО (ТабличнаяЧасть?.Ссылка = #КлючиДоступаКДанным.#КлючДоступаПользователей)"; // @query-part-1
		
		Если ГруппаДополнительныхТаблиц = Неопределено Тогда
			Если ИмяТабличнойЧастиОбъекта = Неопределено Тогда
				ПсевдонимПромежуточнойТаблицы = "ПромежуточнаяТаблица" + НомерТабличнойЧастиКлюча;
				Соединения = Соединения + "
				|ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
				|	ИСТИНА КАК ЗначениеИстина) КАК " + ПсевдонимПромежуточнойТаблицы // @query-part-1 @query-part-2
					  + ТекстСОтступом(СоединенияИПоля.Соединения, "	") + "
				|ПО ИСТИНА" // @query-part-1
				      + ТекстСОтступом(СоединенияИПоля.Поля, "	");
				ИсходноеУсловие = ПсевдонимПромежуточнойТаблицы + ".ЗначениеИстина #ПроверкаNULL";
			Иначе
				Соединения = Соединения + "
				|ЛЕВОЕ СОЕДИНЕНИЕ &ТекущийСписок." + ИмяТабличнойЧастиОбъекта + " КАК " + ПсевдонимТабличнойЧастиОбъекта // @query-part-1 @query-part-2
					  + ТекстСОтступом(СоединенияИПоля.Соединения, "	") + "
				|ПО " + ПсевдонимТабличнойЧастиОбъекта + ".Ссылка = ТекущийСписок.Ссылка" // @query-part-1
				      + ТекстСОтступом(СоединенияИПоля.Поля, "	");
			КонецЕсли;
		Иначе
			ДополнительнаяТаблица = ГруппаДополнительныхТаблиц[0];
			СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить(ДополнительнаяТаблица.Псевдоним);
			Соединения = Соединения + "
			|ЛЕВОЕ СОЕДИНЕНИЕ " + ДополнительнаяТаблица.Таблица + " КАК " + ДополнительнаяТаблица.Псевдоним // @query-part-1 @query-part-2
				  + ТекстСОтступом(СоединенияИПоля.Соединения, "	") + "
			|ПО " + ТекстСОтступом(ДополнительнаяТаблица.ТекстУсловияСоединения, "	") // @query-part-1
			      + ТекстСОтступом(СоединенияИПоля.Поля, "	");
		КонецЕсли;
		Соединения = ТекстСОтступом(Соединения, "		");
		
		Условие =
		"ТабличнаяЧасть?.Ссылка ЕСТЬ НЕ NULL
		|И " + СокрЛ(ИсходноеУсловие); // @query-part-1
		Условие = ТекстСОтступом(Условие, "	");
		Условие = СтрЗаменить(Условие, "#ПроверкаNULL", "ЕСТЬ NULL"); // @query-part-2
	Иначе
		Соединения =
		"ЛЕВОЕ СОЕДИНЕНИЕ Справочник.КлючиДоступа.ТабличнаяЧасть? КАК ТабличнаяЧасть?
		|ПО (ТабличнаяЧасть?.Ссылка = #КлючиДоступаКДанным.#КлючДоступаПользователей)
		|ЛЕВОЕ СОЕДИНЕНИЕ &ТекущийСписок КАК ТекущийСписокИсточник" // @query-part-1
			+ ТекстСОтступом(ИсходныеСоединения, "	") + "
		|ПО " + ?(Контекст.ЭтоСсылочныйТип, "(ТекущийСписокИсточник.Ссылка = ТекущийСписок#.Ссылка)", // @query-part-1
			Контекст.ОпорныеПоля.УсловиеСоединения);
		
		Соединения = Соединения + ТекстСОтступом(Поля, "	");
		Соединения = ТекстСОтступом(Соединения, "		");
		Соединения = СтрЗаменить(Соединения, "ТекущийСписок.", "ТекущийСписокИсточник.");
		Соединения = СтрЗаменить(Соединения, "ТекущийСписок#.", "ТекущийСписок.");
		
		Если Контекст.ЭтоСсылочныйТип Тогда
			Условие =
			"ТабличнаяЧасть?.Ссылка ЕСТЬ НЕ NULL
			|И ТекущийСписокИсточник.Ссылка ЕСТЬ NULL"; // @query-part-1
		Иначе
			Условие =
			"ТабличнаяЧасть?.Ссылка ЕСТЬ НЕ NULL
			|И ТекущийСписокИсточник.Поле1 ЕСТЬ NULL"; // @query-part-1
		КонецЕсли;
		Условие = ТекстСОтступом(Условие, "	");
	КонецЕсли;
	
	ТекстЧастиУсловия = ШаблонТекстаЧастиУсловия;
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "#Соединения",     Соединения);
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "&УсловиеЗапроса", Условие);
	ТекстЧастиУсловия = СтрЗаменить(ТекстЧастиУсловия, "ТабличнаяЧасть?", ИмяТабличнойЧастиКлюча);
	Контекст.ЧастиУсловияПроверки.Добавить(ТекстЧастиУсловия);
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ДобавитьЗаполнениеШапкиКлюча(Контекст, НомерШапки)
	
	Если Не Контекст.ЭтоСсылочныйТип И НомерШапки = 0 Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ТекущийСписок.ТекущаяСсылка КАК ТекущаяСсылка,
		|	&ОпорныеПоляДляВыбора
		|	ПОМЕСТИТЬ ТекущийСписок
		|ИЗ
		|	&ЗначенияОпорныхПолей КАК ТекущийСписок";
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляДляВыбора", Контекст.ОпорныеПоля.ДляВыбора);
		Контекст.ЧастиЗапросаЗначенийИзОбъектов.Добавить(ТекстЗапроса);
		
	ИначеЕсли НомерШапки = 0 Тогда
		Для Каждого ОписаниеТаблицы Из Контекст.ПоляТаблицОбъекта.Результат Цикл
			ТекстЗапроса = 
			"ВЫБРАТЬ
			|	&ПоляТаблицы
			|	ПОМЕСТИТЬ #ПолноеИмяТаблицы
			|ИЗ
			|	&ПолноеИмяТаблицы КАК ТекущаяТаблица";
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляТаблицы", 
				"ТекущаяТаблица." + СтрСоединить(ОписаниеТаблицы.Поля, ",
					|	ТекущаяТаблица."));
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ПолноеИмяТаблицы", ОписаниеТаблицы.ПолноеИмяТаблицы);
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПолноеИмяТаблицы", "&" + ОписаниеТаблицы.ПолноеИмяТаблицы);
			Контекст.ЧастиЗапросаЗначенийИзОбъектовВПамяти.Добавить(ТекстЗапроса);
		КонецЦикла;
	КонецЕсли;
	
	ГруппаПолей = Контекст.ГруппыПолей.Получить("Шапка" + НомерШапки);
	Если ГруппаПолей = Неопределено Тогда
		Возврат;
	КонецЕсли;
	ДобавитьОписаниеТаблицыКлюча("Шапка" + НомерШапки, ГруппаПолей, Контекст);
	
	СоединенияИПоля = СоединенияИПоляПоТаблицам(ГруппаПолей, Ложь,
		НомерШапки, Истина).Получить("ТекущийСписок");
	
	// Выбор значений из объектов для поиска и создания ключей доступа.
	Если Контекст.ЭтоСсылочныйТип Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ТекущийСписок.Ссылка КАК ТекущаяСсылка
		|	,&ПоляЗапроса
		|ИЗ
		|	&ТекущийСписок КАК ТекущийСписок
		|	#СоединенияЗапроса
		|ГДЕ
		|	ТекущийСписок.Ссылка В (&СсылкиНаОбъекты)
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущаяСсылка
		|ИТОГИ ПО
		|	ТекущаяСсылка"; // @query-part-1
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ТекущийСписок.ТекущаяСсылка КАК ТекущаяСсылка
		|	,&ПоляЗапроса
		|ИЗ
		|	ТекущийСписок КАК ТекущийСписок
		|	#СоединенияЗапроса
		|ГДЕ
		|	&УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущаяСсылка, 
		|	&ОпорныеПоляДляУпорядочения
		|ИТОГИ ПО
		|	ТекущаяСсылка"; // @query-part-1
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ОпорныеПоляДляУпорядочения", Контекст.ОпорныеПоля.ДляУпорядочения);
	КонецЕсли;
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&ПоляЗапроса", ТекстСОтступом(СоединенияИПоля.Поля, "	"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#СоединенияЗапроса", ТекстСОтступом(СоединенияИПоля.Соединения, "	"));
	
	Контекст.ЧастиЗапросаЗначенийИзОбъектов.Добавить(ТекстЗапроса);
	Контекст.ЧастиЗапросаЗначенийИзОбъектовВПамяти.Добавить(ТекстЗапроса);
	
	// Выбор значений из ключей доступа для сравнения со значениями требуемых ключей.
	Если НомерШапки = 0 Тогда
		ТекстЗапроса = 
		"ВЫБРАТЬ
		|	Шапка0.Ссылка КАК ТекущаяСсылка
		|	,&Реквизиты
		|ИЗ
		|	Справочник.КлючиДоступа КАК Шапка0
		|ГДЕ
		|	Шапка0.Хеш В(&Хеши)
		|	И Шапка0.Список = &Список
		|	И Шапка0.СоставПолей = &СоставПолей
		|	И Шапка0.ДляВнешнихПользователей = &ДляВнешнихПользователей
		|	И Шапка0.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущаяСсылка
		|ИТОГИ ПО
		|	ТекущаяСсылка";
	Иначе
		
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	Шапка?.Ссылка КАК ТекущаяСсылка
		|	,&Реквизиты
		|ИЗ
		|	Справочник.КлючиДоступа.Шапка КАК Шапка?
		|ГДЕ
		|	&УсловиеНомераШапки
		|	И Шапка?.Ссылка.Хеш В(&Хеши)
		|	И Шапка?.Ссылка.Список = &Список
		|	И Шапка?.Ссылка.СоставПолей = &СоставПолей
		|	И Шапка?.Ссылка.ДляВнешнихПользователей = &ДляВнешнихПользователей
		|	И Шапка?.Ссылка.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
		|	И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	ТекущаяСсылка
		|ИТОГИ ПО
		|	ТекущаяСсылка"; // @query-part
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеНомераШапки", 
			"Шапка" + НомерШапки + ".НомерСтроки = " + НомерШапки);
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&Реквизиты", ТекстСОтступом(СоединенияИПоля.Реквизиты, "	"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "Шапка?", "Шапка" + НомерШапки);
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляСравнения.Добавить(ТекстЗапроса);
	
	// Проверка существования ключа доступа перед записью нового ключа.
	Если НомерШапки = 0 Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ИСТИНА КАК ЗначениеИстина
		|ИЗ
		|	Справочник.КлючиДоступа КАК Шапка0
		|ГДЕ
		|	Шапка0.Хеш = &Хеш
		|	И Шапка0.Список = &Список
		|	И Шапка0.СоставПолей = &СоставПолей
		|	И Шапка0.ДляВнешнихПользователей = &ДляВнешнихПользователей
		|	И &УточнениеПланаЗапроса";
		Контекст.ЧастиЗапросаСуществованияКлючейДляСравнения.Добавить(ТекстЗапроса);
	КонецЕсли;
	
	// Запрос ключей доступа по ведущим ключам для обновления пользователей и групп доступа, которым они разрешены.
	ДобавитьУсловиеОтбораПоВедущимКлючамДоступа(Контекст, ГруппаПолей, НомерШапки);
	
	// Выбор значений из ключей доступа для вычисления пользователей и групп доступа, которым они разрешены.
	Если НомерШапки = 0 Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	КлючиДоступа.Ссылка КАК Ссылка
		|ПОМЕСТИТЬ ПорцияКлючей
		|ИЗ
		|	&КлючиДоступа КАК КлючиДоступа
		|
		|ИНДЕКСИРОВАТЬ ПО
		|	Ссылка";
		Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	КонецЕсли;
	
	Если НомерШапки = 0 Тогда
		ТекстЗапроса = // @query-part-1
		"ВЫБРАТЬ
		|	ПорцияКлючей.Ссылка КАК Ссылка
		|	,&Реквизиты
		|ИЗ
		|	ПорцияКлючей КАК ПорцияКлючей
		|	ЛЕВОЕ СОЕДИНЕНИЕ @Справочник.КлючиДоступа КАК Шапка0
		|	ПО
		|		Шапка0.Ссылка = ПорцияКлючей.Ссылка
		|		И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	Ссылка
		|ИТОГИ ПО
		|	Ссылка"; // @query-part
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	ПорцияКлючей.Ссылка КАК Ссылка
		|	,&Реквизиты
		|ИЗ
		|	ПорцияКлючей КАК ПорцияКлючей
		|	ЛЕВОЕ СОЕДИНЕНИЕ @Справочник.КлючиДоступа.Шапка КАК Шапка?
		|	ПО
		|		Шапка?.Ссылка = ПорцияКлючей.Ссылка
		|		И &УсловиеНомераШапки
		|		И &УточнениеПланаЗапроса
		|
		|УПОРЯДОЧИТЬ ПО
		|	Ссылка
		|ИТОГИ ПО
		|	Ссылка"; // @query-part
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеНомераШапки", 
			"Шапка" + НомерШапки + ".НомерСтроки = " + НомерШапки); 
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&Реквизиты", ТекстСОтступом(СоединенияИПоля.Реквизиты, "	"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "Шапка?", "Шапка" + НомерШапки);
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
	// Условие отбора прав ведущих ключей доступа.
	ДобавитьУсловиеОтбораПравДляШапкиКлюча(Контекст, ГруппаПолей, НомерШапки, "ДляВедущихКлючей");
	
	// Условие отбора прав ведущих списков.
	ДобавитьУсловиеОтбораПравДляШапкиКлюча(Контекст, ГруппаПолей, НомерШапки, "ДляВедущихСписков");
	
	// Условие отбора прав по владельцам настроек прав.
	ДобавитьУсловиеОтбораПравДляШапкиКлюча(Контекст, ГруппаПолей, НомерШапки, "ДляВладельцевНастроекПрав");
	
КонецПроцедуры

// Для процедуры ДобавитьЗаполнениеШапкиКлюча.
Процедура ДобавитьУсловиеОтбораПравДляШапкиКлюча(Контекст, ГруппаПолей, НомерШапки, НазначениеУсловия)
	
	// Условие выбора прав ведущих ключей доступа.
	Для Каждого СвойстваПоля Из ГруппаПолей Цикл
		Если НазначениеУсловия = "ДляВедущихКлючей"
		   И СвойстваПоля.ТипыСохраненияКлючейДоступа.Количество() = 0
		 Или НазначениеУсловия = "ДляВедущихСписков"
		   И Не СвойстваПоля.ЕстьТипВедущегоСписка
		 Или НазначениеУсловия = "ДляВладельцевНастроекПрав"
		   И Не СвойстваПоля.ЕстьТипВладельцаНастроекПрав Тогда
			Продолжить;
		КонецЕсли;
		
		Если НазначениеУсловия = "ДляВедущихСписков"
		   И СвойстваПоля.ТипыСохраненияЗначений.Количество() > 0 Тогда
			
			Контекст.Вставить("ЧастиУсловияВыбораПравВедущихСписковСТипами");
			УсловиеОтбора = // @query-part-1
			"#ПроверяемоеПоле В
			|				(ВЫБРАТЬ
			|					ЕСТЬNULL(ТипыКонфигурации.Ссылка, ЕСТЬNULL(ТипыРасширений.Ссылка, Шапка?.Реквизит?))
			|				ИЗ
			|					ПорцияКлючей КАК ПорцияКлючей
			|						ЛЕВОЕ СОЕДИНЕНИЕ @Справочник.КлючиДоступа КАК Шапка?
			|						ПО
			|							Шапка?.Ссылка = ПорцияКлючей.Ссылка
			|							
			|						ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовМетаданных КАК ТипыКонфигурации
			|						ПО
			|							Шапка?.Реквизит? <> НЕОПРЕДЕЛЕНО
			|							И ТИПЗНАЧЕНИЯ(Шапка?.Реквизит?) <> ТИП(Справочник.ИдентификаторыОбъектовМетаданных)
			|							И ТИПЗНАЧЕНИЯ(Шапка?.Реквизит?) <> ТИП(Справочник.ИдентификаторыОбъектовРасширений)
			|							И ТИПЗНАЧЕНИЯ(ТипыКонфигурации.ЗначениеПустойСсылки) = ТИПЗНАЧЕНИЯ(Шапка?.Реквизит?)
			|							
			|						ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовРасширений КАК ТипыРасширений
			|						ПО
			|							Шапка?.Реквизит? <> НЕОПРЕДЕЛЕНО
			|							И ТИПЗНАЧЕНИЯ(ТипыРасширений.ЗначениеПустойСсылки) = ТИПЗНАЧЕНИЯ(Шапка?.Реквизит?))";
		Иначе
			УсловиеОтбора = // @query-part-1
			"#ПроверяемоеПоле В
			|				(ВЫБРАТЬ
			|					Шапка?.Реквизит?
			|				ИЗ
			|					ПорцияКлючей КАК ПорцияКлючей
			|						ЛЕВОЕ СОЕДИНЕНИЕ @Справочник.КлючиДоступа КАК Шапка?
			|						ПО
			|							Шапка?.Ссылка = ПорцияКлючей.Ссылка)";
		КонецЕсли;
		Если НомерШапки > 0 Тогда
			УсловиеОтбора = СтрЗаменить(УсловиеОтбора,
				"Справочник.КлючиДоступа КАК Шапка?", "Справочник.КлючиДоступа.Шапка КАК Шапка?"); // @query-part-1 @query-part-2
			УсловиеОтбора = СтрЗаменить(УсловиеОтбора,
				"Шапка?.Ссылка = ПорцияКлючей.Ссылка", ТекстСОтступом(
				"Шапка?.Ссылка = ПорцияКлючей.Ссылка
				|	И Шапка?.НомерСтроки = " + НомерШапки, "							")); // @query-part-1
		КонецЕсли;
		УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "Шапка?", "Шапка" + НомерШапки);
		УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "Реквизит?", СвойстваПоля.ИмяРеквизитаГруппыПолейКлючаДоступа);
		
		Если НазначениеУсловия = "ДляВедущихКлючей" Тогда
			Контекст.ЧастиУсловияВыбораПравВедущихКлючейДоступа.Добавить(УсловиеОтбора);
			
		ИначеЕсли НазначениеУсловия = "ДляВедущихСписков" Тогда
			Контекст.ЧастиУсловияВыбораПравВедущихСписков.Добавить(УсловиеОтбора);
		Иначе
			Контекст.ЧастиУсловияВыбораПравПоВладельцамНастроекПрав.Добавить(УсловиеОтбора);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ДобавитьВыборКлючейБезПолейВШапке(Контекст)
	
	Если Контекст.ГруппыПолей.Получить("Шапка0") <> Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	// Выбор значений из ключей доступа для сравнения со значениями требуемых ключей.
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	КлючиДоступа.Ссылка КАК ТекущаяСсылка
	|ИЗ
	|	Справочник.КлючиДоступа КАК КлючиДоступа
	|ГДЕ
	|	КлючиДоступа.Хеш В(&Хеши)
	|	И КлючиДоступа.Список = &Список
	|	И КлючиДоступа.СоставПолей = &СоставПолей
	|	И КлючиДоступа.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И КлючиДоступа.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
	|	И &УточнениеПланаЗапроса
	|
	|УПОРЯДОЧИТЬ ПО
	|	ТекущаяСсылка";
	
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляСравнения.Добавить(ТекстЗапроса);
	
	// Проверка существования ключа доступа перед записью нового ключа.
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ИСТИНА КАК ЗначениеИстина
	|ИЗ
	|	Справочник.КлючиДоступа КАК Шапка0
	|ГДЕ
	|	Шапка0.Хеш = &Хеш
	|	И Шапка0.Список = &Список
	|	И Шапка0.СоставПолей = &СоставПолей
	|	И Шапка0.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И &УточнениеПланаЗапроса";
	Контекст.ЧастиЗапросаСуществованияКлючейДляСравнения.Добавить(ТекстЗапроса);
	
	// Выбор значений из ключей доступа для вычисления пользователей и групп доступа, которым они разрешены.
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	КлючиДоступа.Ссылка КАК Ссылка
	|ПОМЕСТИТЬ ПорцияКлючей
	|ИЗ
	|	&КлючиДоступа КАК КлючиДоступа
	|
	|ИНДЕКСИРОВАТЬ ПО
	|	Ссылка";
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
КонецПроцедуры

// Для процедуры ДобавитьТекстыЗапросовВПараметрыОграничения.
Процедура ДобавитьЗаполнениеТабличнойЧастиКлюча(Контекст, НомерТабличнойЧастиКлюча)
	
	ТаблицыПоГруппам  = Контекст.ГруппыДополнительныхТаблиц.ТаблицыПоГруппам;
	ГруппаДополнительныхТаблиц = ТаблицыПоГруппам.Получить(НомерТабличнойЧастиКлюча
		- (Контекст.КоличествоТабличныхЧастейКлюча - ТаблицыПоГруппам.Количество()));
	
	ИмяТабличнойЧастиКлюча = "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча;
	ГруппаПолей = Контекст.ГруппыПолей.Получить(ИмяТабличнойЧастиКлюча);
	
	ДобавитьОписаниеТаблицыКлюча(ИмяТабличнойЧастиКлюча, ГруппаПолей, Контекст);
	
	СоединенияИПоляПоТаблицам = СоединенияИПоляПоТаблицам(ГруппаПолей,
		Истина, , Истина, ВГруппеОднаДополнительнаяТаблицаСПолями(Контекст, ГруппаДополнительныхТаблиц));
	
	// Выбор значений из объектов для поиска и создания ключей доступа.
	Если ГруппаДополнительныхТаблиц = Неопределено Тогда
		ПсевдонимТабличнойЧастиОбъекта = Контекст.ПсевдонимыТабличныхЧастейОбъекта.Получить(НомерТабличнойЧастиКлюча);
		Если ПсевдонимТабличнойЧастиОбъекта = Неопределено Тогда
			СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить("ТекущийСписок");
		Иначе
			СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить(ПсевдонимТабличнойЧастиОбъекта);
		КонецЕсли;
		ПоляВыбора = ТекстСОтступом(СоединенияИПоля.Поля, "	");
		Соединения = ТекстСОтступом(СоединенияИПоля.Соединения, "	");
		ПоляУпорядочения = СоединенияИПоля.ПоляУпорядочения;
		Реквизиты        = СоединенияИПоля.Реквизиты;
		Если ПсевдонимТабличнойЧастиОбъекта = Неопределено Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ТекущийСписок.Ссылка КАК ТекущаяСсылка
			|	,&ПоляВыбора
			|ИЗ
			|	&ТекущийСписок КАК ТекущийСписок
			|	#Соединения
			|ГДЕ
			|	ТекущийСписок.Ссылка В (&СсылкиНаОбъекты)
			|
			|УПОРЯДОЧИТЬ ПО
			|	ТекущаяСсылка
			|	,&ПоляУпорядочения
			|ИТОГИ ПО
			|	ТекущаяСсылка"; // @query-part-1
		Иначе
			ИмяТабличнойЧастиОбъекта = СтрЗаменить(ПсевдонимТабличнойЧастиОбъекта, "ТекущийСписок", "");
			ТекстЗапроса =
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ПсевдонимТабличнойЧастиОбъекта.Ссылка КАК ТекущаяСсылка
			|	,&ПоляВыбора
			|ИЗ
			|	ИмяТабличнойЧастиОбъекта КАК ПсевдонимТабличнойЧастиОбъекта
			|	#Соединения
			|ГДЕ
			|	ПсевдонимТабличнойЧастиОбъекта.Ссылка В (&СсылкиНаОбъекты)
			|
			|УПОРЯДОЧИТЬ ПО
			|	ТекущаяСсылка
			|	,&ПоляУпорядочения
			|ИТОГИ ПО
			|	ТекущаяСсылка"; // @query-part-1

			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ПсевдонимТабличнойЧастиОбъекта", ПсевдонимТабличнойЧастиОбъекта); 
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ИмяТабличнойЧастиОбъекта", "&ТекущийСписок." + ИмяТабличнойЧастиОбъекта); 
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&ПоляВыбора", ПоляВыбора); 
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&ПоляУпорядочения", ПоляУпорядочения); 
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#Соединения", Соединения); 
	Иначе
		ПоляВыбора = "";
		Соединения = "";
		СоединенияВБазеДанных = "";
		СоединенияВПамяти = "";
		Условие = "";
		ПоляУпорядочения = "";
		Реквизиты = "";
		Для Каждого ДополнительнаяТаблица Из ГруппаДополнительныхТаблиц Цикл
			СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить(ДополнительнаяТаблица.Псевдоним);
			// @query-part-1
			ТекущееСоединение = "
			|ЛЕВОЕ СОЕДИНЕНИЕ " + ДополнительнаяТаблица.Таблица + " КАК " + ДополнительнаяТаблица.Псевдоним + "
			|ПО " + ТекстСОтступом(ДополнительнаяТаблица.ТекстУсловияСоединения, "	"); // @query-part-1
			Соединения = Соединения + ТекущееСоединение;
			Если Контекст.ЭтоСсылочныйТип Тогда
				Если ВРег(Контекст.Список) = ВРег(ДополнительнаяТаблица.Таблица) Тогда
					ДополнительноеИмя = "&ТекущийСписок";
				ИначеЕсли СтрНачинаетсяС(ВРег(ДополнительнаяТаблица.Таблица), ВРег(Контекст.Список) + ".") Тогда
					ДополнительноеИмя = "&ТекущийСписок." + СтрРазделить(ДополнительнаяТаблица.Таблица, ".")[2];
				Иначе
					ДополнительноеИмя = ДополнительнаяТаблица.Таблица;
				КонецЕсли;
				Если ДополнительноеИмя = ДополнительнаяТаблица.Таблица Тогда
					СоединенияВБазеДанных = СоединенияВБазеДанных  + ТекущееСоединение;
					СоединенияВПамяти     = СоединенияВПамяти      + ТекущееСоединение;
				Иначе
					// @query-part-1
					СоединенияВБазеДанных = СоединенияВБазеДанных + "
					|ЛЕВОЕ СОЕДИНЕНИЕ " + ДополнительнаяТаблица.Таблица + " КАК " + ДополнительнаяТаблица.Псевдоним + "
					|ПО " + ТекстСОтступом(ДополнительнаяТаблица.ТекстУсловияСоединения, "	") + "
					|	И НЕ " + ДополнительнаяТаблица.Псевдоним + ".Ссылка В (&СсылкиНаОбъекты)"; // @query-part-1 @query-part-2
					// @query-part-1
					СоединенияВПамяти = СоединенияВПамяти + "
					|ЛЕВОЕ СОЕДИНЕНИЕ " + ДополнительноеИмя + " КАК " + ДополнительнаяТаблица.Псевдоним + "
					|ПО " + ТекстСОтступом(ДополнительнаяТаблица.ТекстУсловияСоединения, "	"); // @query-part-1
				КонецЕсли;
			КонецЕсли;
			Если СоединенияИПоля = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			Соединения = Соединения + СоединенияИПоля.Соединения;
			Если Контекст.ЭтоСсылочныйТип Тогда
				СоединенияВБазеДанных = СоединенияВБазеДанных + СоединенияИПоля.Соединения;
				СоединенияВПамяти     = СоединенияВПамяти     + СоединенияИПоля.Соединения;
			КонецЕсли;
			Условие = Условие + ?(Условие = "", "", "
			|ИЛИ ") + ДополнительнаяТаблица.ПолеПроверкиСоединения + " ЕСТЬ НЕ NULL"; // @query-part-1 @query-part-2
			ПоляВыбора       = ПоляВыбора       + СоединенияИПоля.Поля;
			ПоляУпорядочения = ПоляУпорядочения + СоединенияИПоля.ПоляУпорядочения;
			Реквизиты        = Реквизиты        + СоединенияИПоля.Реквизиты;
		КонецЦикла;
		Если СтрЧислоСтрок(Условие) > 1 Тогда
			Условие = "(" + Условие + ")";
		КонецЕсли;
		Если Контекст.ЭтоСсылочныйТип Тогда
			ТекстЗапроса = // @query-part-1
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	ТекущийСписок.Ссылка КАК ТекущаяСсылка" + ТекстСОтступом(ПоляВыбора, "	") + "
			|ИЗ
			|	&ТекущийСписок КАК ТекущийСписок" + ТекстСОтступом(Соединения, "	") + "
			|ГДЕ
			|	ТекущийСписок.Ссылка В (&СсылкиНаОбъекты)
			|	И " + ТекстСОтступом(Условие, "	") + "
			|
			|УПОРЯДОЧИТЬ ПО
			|	ТекущаяСсылка" + ПоляУпорядочения + "
			|ИТОГИ ПО
			|	ТекущаяСсылка"; // @query-part-1
			Если СоединенияВПамяти <> СоединенияВБазеДанных Тогда
				// АПК:96-выкл - №434 Использование ОБЪЕДИНИТЬ допустимо, так как
				// строки не должны повторятся и объем данных небольшой.
				ТекстЗапросаВПамяти = // @query-part-1
				"ВЫБРАТЬ РАЗЛИЧНЫЕ
				|	ТекущийСписок.Ссылка КАК ТекущаяСсылка" + ТекстСОтступом(ПоляВыбора, "	") + "
				|ИЗ
				|	&ТекущийСписок КАК ТекущийСписок" + ТекстСОтступом(СоединенияВБазеДанных, "	") + "
				|ГДЕ
				|	ТекущийСписок.Ссылка В (&СсылкиНаОбъекты)
				|	И " + ТекстСОтступом(Условие, "	") + "
				|
				|ОБЪЕДИНИТЬ
				|
				|ВЫБРАТЬ РАЗЛИЧНЫЕ
				|	ТекущийСписок.Ссылка КАК ТекущаяСсылка" + ТекстСОтступом(ПоляВыбора, "	") + "
				|ИЗ
				|	&ТекущийСписок КАК ТекущийСписок" + ТекстСОтступом(СоединенияВПамяти, "	") + "
				|ГДЕ
				|	ТекущийСписок.Ссылка В (&СсылкиНаОбъекты)
				|	И " + ТекстСОтступом(Условие, "	") + "
				|
				|УПОРЯДОЧИТЬ ПО
				|	ТекущаяСсылка" + ПоляУпорядочения + "
				|ИТОГИ ПО
				|	ТекущаяСсылка"; // @query-part-1
				// АПК:96-вкл.
			КонецЕсли;
		Иначе
			ТекстЗапроса = // @query-part-1
			"ВЫБРАТЬ
			|	ТекущийСписок.ТекущаяСсылка КАК ТекущаяСсылка"  + ТекстСОтступом(ПоляВыбора, "	") + "
			|ИЗ
			|	ТекущийСписок КАК ТекущийСписок" + ТекстСОтступом(Соединения, "	") + "
			|ГДЕ
			|	" + ТекстСОтступом(Условие, "	") + "
			|
			|УПОРЯДОЧИТЬ ПО
			|	ТекущаяСсылка, " + Контекст.ОпорныеПоля.ДляУпорядочения + ПоляУпорядочения + "
			|ИТОГИ ПО
			|	ТекущаяСсылка"; // @query-part-1
		КонецЕсли;
	КонецЕсли;
	
	Контекст.ЧастиЗапросаЗначенийИзОбъектов.Добавить(ТекстЗапроса);
	Контекст.ЧастиЗапросаЗначенийИзОбъектовВПамяти.Добавить(
		?(ЗначениеЗаполнено(ТекстЗапросаВПамяти), ТекстЗапросаВПамяти, ТекстЗапроса));
	
	// Запрос ключей доступа по ведущим ключам для обновления пользователей и групп доступа, которым они разрешены.
	ДобавитьУсловиеОтбораПоВедущимКлючамДоступа(Контекст, ГруппаПолей, , НомерТабличнойЧастиКлюча);
	
	// Выбор значений из ключей доступа для сравнения со значениями требуемых ключей.
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ТабличнаяЧасть?.Ссылка КАК ТекущаяСсылка
	|	,&Реквизиты
	|ИЗ
	|	Справочник.КлючиДоступа.ТабличнаяЧасть? КАК ТабличнаяЧасть?
	|ГДЕ
	|	ТабличнаяЧасть?.Ссылка.Хеш В(&Хеши)
	|	И ТабличнаяЧасть?.Ссылка.Список = &Список
	|	И ТабличнаяЧасть?.Ссылка.СоставПолей = &СоставПолей
	|	И ТабличнаяЧасть?.Ссылка.ДляВнешнихПользователей = &ДляВнешнихПользователей
	|	И ТабличнаяЧасть?.Ссылка.НеИспользуетсяС = ДАТАВРЕМЯ(1, 1, 1)
	|	И &УточнениеПланаЗапроса
	|
	|УПОРЯДОЧИТЬ ПО
	|	ТекущаяСсылка
	|	,&ПоляУпорядочения
	|ИТОГИ ПО
	|	ТекущаяСсылка"; // @query-part
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&Реквизиты", ТекстСОтступом(Реквизиты, "	"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&ПоляУпорядочения", ПоляУпорядочения);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТабличнаяЧасть?", "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча);
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляСравнения.Добавить(ТекстЗапроса);
	
	// Выбор значений из ключей доступа для вычисления пользователей и групп доступа, которым они разрешены.
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	ПорцияКлючей.Ссылка КАК Ссылка
	|	,&Реквизиты
	|ИЗ
	|	ПорцияКлючей КАК ПорцияКлючей
	|	ЛЕВОЕ СОЕДИНЕНИЕ @Справочник.КлючиДоступа.ТабличнаяЧасть? КАК ТабличнаяЧасть?
	|	ПО
	|		ТабличнаяЧасть?.Ссылка = ПорцияКлючей.Ссылка
	|
	|УПОРЯДОЧИТЬ ПО
	|	Ссылка
	|ИТОГИ ПО
	|	Ссылка"; // @query-part-1
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, ",&Реквизиты", ТекстСОтступом(Реквизиты, "	"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТабличнаяЧасть?", "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча);
	Контекст.ЧастиЗапросаЗначенийИзКлючейДляРасчетаПрав.Добавить(ТекстЗапроса);
	
	// Условие отбора прав ведущих ключей доступа.
	ДобавитьУсловиеОтбораПравДляТабличнойЧастиКлюча(Контекст,
		ГруппаПолей, НомерТабличнойЧастиКлюча, "ДляВедущихКлючей");
	
	// Условие отбора прав ведущих списков.
	ДобавитьУсловиеОтбораПравДляТабличнойЧастиКлюча(Контекст,
		ГруппаПолей, НомерТабличнойЧастиКлюча, "ДляВедущихСписков");
	
	// Условие отбора прав по владельцам настроек прав.
	ДобавитьУсловиеОтбораПравДляТабличнойЧастиКлюча(Контекст,
		ГруппаПолей, НомерТабличнойЧастиКлюча, "ДляВладельцевНастроекПрав");
	
КонецПроцедуры

// Для процедуры ДобавитьЗаполнениеТабличнойЧастиКлюча.
Процедура ДобавитьУсловиеОтбораПравДляТабличнойЧастиКлюча(Контекст, ГруппаПолей,
			НомерТабличнойЧастиКлюча, НазначениеУсловия)
	
	Для Каждого СвойстваПоля Из ГруппаПолей Цикл
		Если НазначениеУсловия = "ДляВедущихКлючей"
		   И СвойстваПоля.ТипыСохраненияКлючейДоступа.Количество() = 0
		 Или НазначениеУсловия = "ДляВедущихСписков"
		   И Не СвойстваПоля.ЕстьТипВедущегоСписка
		 Или НазначениеУсловия = "ДляВладельцевНастроекПрав"
		   И Не СвойстваПоля.ЕстьТипВладельцаНастроекПрав Тогда
			Продолжить;
		КонецЕсли;
		
		Если НазначениеУсловия = "ДляВедущихСписков"
		   И СвойстваПоля.ТипыСохраненияЗначений.Количество() > 0 Тогда
			
			Контекст.Вставить("ЧастиУсловияВыбораПравВедущихСписковСТипами");
			УсловиеОтбора = // @query-part-1
			"#ПроверяемоеПоле В
			|				(ВЫБРАТЬ
			|					ЕСТЬNULL(ТипыКонфигурации.Ссылка, ЕСТЬNULL(ТипыРасширений.Ссылка, ТабличнаяЧасть?.Реквизит?))
			|				ИЗ
			|					ПорцияКлючей КАК ПорцияКлючей
			|						ЛЕВОЕ СОЕДИНЕНИЕ @Справочник.КлючиДоступа.ТабличнаяЧасть? КАК ТабличнаяЧасть?
			|						ПО
			|							ТабличнаяЧасть?.Ссылка = ПорцияКлючей.Ссылка
			|							
			|						ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовМетаданных КАК ТипыКонфигурации
			|						ПО
			|							ТабличнаяЧасть?.Реквизит? <> НЕОПРЕДЕЛЕНО
			|							И ТИПЗНАЧЕНИЯ(ТабличнаяЧасть?.Реквизит?) <> ТИП(Справочник.ИдентификаторыОбъектовМетаданных)
			|							И ТИПЗНАЧЕНИЯ(ТабличнаяЧасть?.Реквизит?) <> ТИП(Справочник.ИдентификаторыОбъектовРасширений)
			|							И ТИПЗНАЧЕНИЯ(ТипыКонфигурации.ЗначениеПустойСсылки) = ТИПЗНАЧЕНИЯ(ТабличнаяЧасть?.Реквизит?)
			|							
			|						ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовРасширений КАК ТипыРасширений
			|						ПО
			|							ТабличнаяЧасть?.Реквизит? <> НЕОПРЕДЕЛЕНО
			|							И ТИПЗНАЧЕНИЯ(ТипыРасширений.ЗначениеПустойСсылки) = ТИПЗНАЧЕНИЯ(ТабличнаяЧасть?.Реквизит?))";
		Иначе
			УсловиеОтбора = // @query-part-1
			"#ПроверяемоеПоле В
			|				(ВЫБРАТЬ
			|					ТабличнаяЧасть?.Реквизит?
			|				ИЗ
			|					ПорцияКлючей КАК ПорцияКлючей
			|						ЛЕВОЕ СОЕДИНЕНИЕ @Справочник.КлючиДоступа.ТабличнаяЧасть? КАК ТабличнаяЧасть?
			|						ПО
			|							ТабличнаяЧасть?.Ссылка = ПорцияКлючей.Ссылка)";
		КонецЕсли;
		
		УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "ТабличнаяЧасть?", "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча);
		УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "Реквизит?", СвойстваПоля.ИмяРеквизитаГруппыПолейКлючаДоступа);
		
		Если НазначениеУсловия = "ДляВедущихКлючей" Тогда
			Контекст.ЧастиУсловияВыбораПравВедущихКлючейДоступа.Добавить(УсловиеОтбора);
			
		ИначеЕсли НазначениеУсловия = "ДляВедущихСписков" Тогда
			Контекст.ЧастиУсловияВыбораПравВедущихСписков.Добавить(УсловиеОтбора);
		Иначе
			Контекст.ЧастиУсловияВыбораПравПоВладельцамНастроекПрав.Добавить(УсловиеОтбора);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ДобавитьЗаполнениеШапкиКлюча, ДобавитьЗаполнениеТабличнойЧастиКлюча.
Процедура ДобавитьОписаниеТаблицыКлюча(ИмяТаблицыКлюча, ГруппаПолей, Контекст)
	
	ПоляТаблицыКлюча = Новый Массив;
	
	Для Каждого СвойстваПоля Из ГруппаПолей Цикл
		ПоляТаблицыКлюча.Добавить(СвойстваПоля.ИмяРеквизитаГруппыПолейКлючаДоступа);
	КонецЦикла;
	
	Контекст.ТаблицыКлюча.Добавить(ИмяТаблицыКлюча);
	Контекст.РеквизитыТаблицКлюча.Вставить(ИмяТаблицыКлюча, ПоляТаблицыКлюча);
	
КонецПроцедуры

// Для процедуры ДобавитьЗаполнениеШапкиКлюча, ДобавитьЗаполнениеТабличнойЧастиКлюча.
Процедура ДобавитьУсловиеОтбораПоВедущимКлючамДоступа(Контекст, ГруппаПолей, НомерШапки = 0, НомерТабличнойЧастиКлюча = 0)
	
	УсловиеОтбора = "";
	НомерРеквизита = ?(НомерШапки = 0, 0, 5);
	
	Для Каждого СвойстваПоля Из ГруппаПолей Цикл
		НомерРеквизита = НомерРеквизита + 1;
		
		Если СвойстваПоля.ТипыСохраненияКлючейДоступа.Количество() = 0 Тогда
			Продолжить;
		КонецЕсли;
		
		УсловиеОтбора = // @query-part-1
		"КлючиДоступа.Ссылка В
		|				(ВЫБРАТЬ
		|					Шапка?.Ссылка
		|				ИЗ
		|					Справочник.КлючиДоступа КАК Шапка?
		|				ГДЕ
		|					Шапка?.Значение? В (&ВедущиеКлючиДоступа))";
		
		УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "Значение?", "Значение" + НомерРеквизита);
		
		Если НомерШапки > 0 Тогда
			УсловиеОтбора = СтрЗаменить(УсловиеОтбора,
				"Справочник.КлючиДоступа КАК Шапка?", "Справочник.КлючиДоступа.Шапка КАК Шапка?"); // @query-part-1 @query-part-2
		КонецЕсли;
		
		Если НомерТабличнойЧастиКлюча = 0 Тогда
			УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "Шапка?", "Шапка" + НомерШапки);
		Иначе
			ИмяТабличнойЧасти = "ТабличнаяЧасть" + НомерТабличнойЧастиКлюча;
			УсловиеОтбора = СтрЗаменить(УсловиеОтбора, " КАК", "." + ИмяТабличнойЧасти + " КАК"); // @query-part-1 @query-part-3
			УсловиеОтбора = СтрЗаменить(УсловиеОтбора, "Шапка?", ИмяТабличнойЧасти);
		КонецЕсли;
		
		Контекст.ЧастиУсловияОтбораПоВедущимКлючамДоступа.Добавить(УсловиеОтбора);
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ДобавитьПроверкуШапкиКлюча, ДобавитьПроверкуТабличнойЧастиКлюча.
Функция СоединенияИПоляПоТаблицам(ГруппаПолей, ТабличнаяЧастьКлюча, НомерШапки = 0, ДляВыбораЗначений = Ложь,
			ВГруппеОднаДополнительнаяТаблицаСПолями = Ложь)
	
	СоединенияИПоляПоТаблицам = Новый Соответствие;
	НомерРеквизита = 1 + ?(НомерШапки = 0, 0, 5);
	
	Для Каждого СвойстваПоля Из ГруппаПолей Цикл
		
		СоединенияИПоля = СоединенияИПоляПоТаблицам.Получить(СвойстваПоля.ПсевдонимТаблицы);
		Если СоединенияИПоля = Неопределено Тогда
			СоединенияИПоля = Новый Структура;
			СоединенияИПоля.Вставить("Соединения",       "");
			СоединенияИПоля.Вставить("Поля",             "");
			СоединенияИПоля.Вставить("ПоляУпорядочения", "");
			СоединенияИПоля.Вставить("Реквизиты",        "");
			СоединенияИПоляПоТаблицам.Вставить(СвойстваПоля.ПсевдонимТаблицы, СоединенияИПоля);
		КонецЕсли;
		
		Соединения = "";
		Если СвойстваПоля.ТипыСохраненияКлючейДоступа.Количество() > 0 Тогда
			// @query-part-1
			Соединения = Соединения + "
			|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КлючиДоступаКОбъектам КАК КлючиДоступаКОбъектам?
			|ПО (КлючиДоступаКОбъектам?.Объект = #ИмяПоляДляЗапроса)";
		КонецЕсли;
		
		Если СвойстваПоля.ТипыСохраненияГруппЗначений.Количество() > 0 Тогда
			Если Не СвойстваПоля.ЭтоСписокЗначенийДоступаСГруппамиЗначений Тогда
				// @query-part-1
				Соединения = Соединения + "
				|ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ГруппыЗначенийДоступа КАК ГруппыЗначений?
				|ПО (ГруппыЗначений?.ЗначениеДоступа = #ИмяПоляДляЗапроса)
				|	И (ГруппыЗначений?.ГруппаДанных = 0)";
			ИначеЕсли СвойстваПоля.НесколькоГруппЗначений Тогда
				// @query-part-1
				Соединения = Соединения + "
				|ЛЕВОЕ СОЕДИНЕНИЕ &ТекущийСписок.ГруппыДоступа КАК ГруппыЗначений?
				|ПО (ГруппыЗначений?.Ссылка = #ИмяПоляДляЗапроса)";
			КонецЕсли;
		КонецЕсли;
		
		Если СвойстваПоля.ТипыСохраненияТиповКонфигурации.Количество() > 0 Тогда
			// @query-part-1
			Соединения = Соединения + "
			|ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовМетаданных КАК ТипыКонфигурации?
			|ПО (#ИмяПоляДляЗапроса <> НЕОПРЕДЕЛЕНО)
			|	И (ТИПЗНАЧЕНИЯ(ТипыКонфигурации?.ЗначениеПустойСсылки) = ТИПЗНАЧЕНИЯ(#ИмяПоляДляЗапроса))";
		КонецЕсли;
		
		Если СвойстваПоля.ТипыСохраненияТиповРасширений.Количество() > 0 Тогда
			// @query-part-1
			Соединения = Соединения + "
			|ЛЕВОЕ СОЕДИНЕНИЕ Справочник.ИдентификаторыОбъектовРасширений КАК ТипыРасширений?
			|ПО (#ИмяПоляДляЗапроса <> НЕОПРЕДЕЛЕНО)
			|	И (ТИПЗНАЧЕНИЯ(ТипыРасширений?.ЗначениеПустойСсылки) = ТИПЗНАЧЕНИЯ(#ИмяПоляДляЗапроса))";
		КонецЕсли;
		
		Поле = СравнениеПоля(СвойстваПоля, ВГруппеОднаДополнительнаяТаблицаСПолями);
		
		Если ДляВыбораЗначений Тогда
			Поле = СтрЗаменить(Поле, "Шапка?.Значение? = ", "");
			Поле = СтрЗаменить(Поле, "						", "			");
			Позиция = СтрДлина(Символы.ПС + "И ()"); // @query-part-1
			Поле = "," + Символы.ПС + Сред(Поле, Позиция, СтрДлина(Поле) - Позиция) + " КАК " + "Значение?"; // @query-part-2
			
			СоединенияИПоля.ПоляУпорядочения = СоединенияИПоля.ПоляУпорядочения
				+ ", " + "Значение" + НомерРеквизита;
			
			СоединенияИПоля.Реквизиты  = СоединенияИПоля.Реквизиты + ",
			|Шапка?.Значение"  + НомерРеквизита + " КАК " + "Значение" + НомерРеквизита; // @query-part-2
		КонецЕсли;
		
		ЗаполнитьПсевдонимПоНомеруРеквизита(Соединения, Поле, НомерРеквизита, "Значение?");
		ЗаполнитьПсевдонимПоНомеруРеквизита(Соединения, Поле, НомерРеквизита, "ГруппыЗначений?");
		ЗаполнитьПсевдонимПоНомеруРеквизита(Соединения, Поле, НомерРеквизита, "КлючиДоступаКОбъектам?");
		ЗаполнитьПсевдонимПоНомеруРеквизита(Соединения, Поле, НомерРеквизита, "ТипыКонфигурации?");
		ЗаполнитьПсевдонимПоНомеруРеквизита(Соединения, Поле, НомерРеквизита, "ТипыРасширений?");
		
		Соединения = СтрЗаменить(Соединения, "#ИмяПоляДляЗапроса", СвойстваПоля.ИмяПоляДляЗапроса);
		Поле       = СтрЗаменить(Поле,       "#ИмяПоляДляЗапроса", СвойстваПоля.ИмяПоляДляЗапроса);
		
		Если ТабличнаяЧастьКлюча Тогда
			Поле = СтрЗаменить(Поле, "Шапка?", "ТабличнаяЧасть?");
			СоединенияИПоля.Реквизиты = СтрЗаменить(СоединенияИПоля.Реквизиты, "Шапка?", "ТабличнаяЧасть?");
		КонецЕсли;
		
		СоединенияИПоля.Соединения = СоединенияИПоля.Соединения + Соединения;
		СоединенияИПоля.Поля       = СоединенияИПоля.Поля       + Поле;
		НомерРеквизита = НомерРеквизита + 1;
	КонецЦикла;
	
	Возврат СоединенияИПоляПоТаблицам;
	
КонецФункции

// Для функции СоединенияИПоляПоТаблицам.
Функция ВГруппеОднаДополнительнаяТаблицаСПолями(Контекст, ГруппаДополнительныхТаблиц)
	
	Если ГруппаДополнительныхТаблиц = Неопределено Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если ГруппаДополнительныхТаблиц.Количество() = 1 Тогда
		Возврат Истина;
	КонецЕсли;
	
	ПсевдонимыТаблицСПолями = Контекст.ГруппыДополнительныхТаблиц.ПсевдонимыТаблицСПолями;
	
	КоличествоДополнительныхТаблицСПолями = 0;
	Для Каждого ДополнительнаяТаблица Из ГруппаДополнительныхТаблиц Цикл
		Если ПсевдонимыТаблицСПолями.Получить(ДополнительнаяТаблица.Псевдоним) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		КоличествоДополнительныхТаблицСПолями = КоличествоДополнительныхТаблицСПолями + 1;
	КонецЦикла;
	
	Возврат КоличествоДополнительныхТаблицСПолями = 1;
	
КонецФункции

// Для функции СоединенияИПоляПоТаблицам.
Функция СравнениеПоля(СвойстваПоля, ВГруппеОднаДополнительнаяТаблицаСПолями)
	
	КоличествоТиповПоля = СвойстваПоля.ТипКонечногоПоля.Типы().Количество();
	
	БезЗначенияНеопределено = КоличествоТиповПоля = 1;
	Если СвойстваПоля.Свойство("БезЗначенияNull") Тогда
		БезЗначенияNull = СвойстваПоля.БезЗначенияNull;
	Иначе
		БезЗначенияNull = БезЗначенияNull(СвойстваПоля, ВГруппеОднаДополнительнаяТаблицаСПолями);
	КонецЕсли;
	
	БезУточненияНеопределено = Не СвойстваПоля.ЕстьУточнениеНеопределено Или БезЗначенияНеопределено;
	БезУточненияNull         = Не СвойстваПоля.ЕстьУточнениеNull         Или БезЗначенияNull;
	
	// Сохранение только ключей доступа.
	Если СвойстваПоля.ТипыСохраненияПустойСсылки.Количество() = 0
	   И БезУточненияНеопределено
	   И БезУточненияNull
	   И СвойстваПоля.ТипыСохраненияКлючейДоступа.Количество() = КоличествоТиповПоля Тогда
		Возврат "
		|И (Шапка?.Значение? = ЕСТЬNULL(КлючиДоступаКОбъектам?.#КлючДоступаПользователейКОбъекту,
		|						ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null)))"; // @query-part-1
	КонецЕсли;
	
	// Сохранение только групп значений доступа.
	Если СвойстваПоля.ТипыСохраненияПустойСсылки.Количество() = 0
	   И БезУточненияНеопределено
	   И БезУточненияNull
	   И СвойстваПоля.ТипыСохраненияГруппЗначений.Количество() = КоличествоТиповПоля Тогда
		
		Если Не СвойстваПоля.ЭтоСписокЗначенийДоступаСГруппамиЗначений Тогда
			Возврат "
			|И (Шапка?.Значение? = ЕСТЬNULL(ГруппыЗначений?.ГруппаЗначенийДоступа,
			|						ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null)))"; // @query-part-1
		ИначеЕсли СвойстваПоля.НесколькоГруппЗначений Тогда
			Возврат "
			|И (Шапка?.Значение? = ЕСТЬNULL(ГруппыЗначений?.ГруппаДоступа,
			|						ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null)))"; // @query-part-1
		Иначе
			Возврат "
			|И (Шапка?.Значение? = ЕСТЬNULL(ТекущийСписок.ГруппаДоступа,
			|						ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null)))"; // @query-part-1
		КонецЕсли;
	КонецЕсли;
	
	// Сохранение только значений.
	Если Не ЕстьПростойТип(СвойстваПоля.ТипКонечногоПоля)
	   И КоличествоТиповПоля = 1
	   И СвойстваПоля.ТипыСохраненияЗначений.Количество() = КоличествоТиповПоля Тогда
		
		Если БезЗначенияNull Тогда
			Возврат "
			|И (Шапка?.Значение? = #ИмяПоляДляЗапроса)"; // @query-part-1
		Иначе
			Возврат "
			|И (Шапка?.Значение? = ЕСТЬNULL(#ИмяПоляДляЗапроса,
			|						ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null)))";  // @query-part-1
		КонецЕсли;
	КонецЕсли;
	
	// Сохранение одного ссылочного типа.
	Если БезУточненияNull
	   И КоличествоТиповПоля = 1
	   И СвойстваПоля.ТипыСохраненияТипов.Количество() = КоличествоТиповПоля
	   И СвойстваПоля.ТипыСохраненияТиповПростых.Количество() = 0 Тогда
		
		Если СвойстваПоля.ТипыСохраненияТиповРасширений.Количество() = 0 Тогда
			Возврат "
			|И (Шапка?.Значение? = ЕСТЬNULL(ТипыКонфигурации?.Ссылка,
			|						ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка)))"; // @query-part-1
		ИначеЕсли СвойстваПоля.ТипыСохраненияТиповКонфигурации.Количество() = 0 Тогда
			Возврат "
			|И (Шапка?.Значение? = ЕСТЬNULL(ТипыРасширений?.Ссылка,
			|						ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовРасширений.ПустаяСсылка)))"; // @query-part-1
		КонецЕсли;
	КонецЕсли;
	
	// Сохранение одного простого типа.
	Если БезУточненияNull
	   И КоличествоТиповПоля = 1
	   И СвойстваПоля.ТипыСохраненияТиповПростых.Количество() = 1 Тогда
		
		Возврат "
		|И (Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ТипРазрешенный))"; // @query-part-1
	КонецЕсли;
	
	Если СвойстваПоля.ТипыСохраненияТипаЗапрещенный.Количество() = КоличествоТиповПоля Тогда
		Возврат "
		|И (Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ТипЗапрещенный))"; // @query-part-1
	КонецЕсли;
	
	// Сохранение только значения ТипРазрешенный.
	Если СвойстваПоля.ТипыСохраненияТипаРазрешенный.Количество() = КоличествоТиповПоля Тогда
		Возврат "
		|И (Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ТипРазрешенный))"; // @query-part-1
	КонецЕсли;
	
	СравнениеПоля = "
	|И (ВЫБОР
	|	#СодержаниеВыбора
	|КОНЕЦ)"; // @query-part-1
	СодержаниеВыбора = "";
	
	СохранениеЗначенияБулево = СвойстваПоля.ТипыСохраненияЗначений.Найти(Тип("Булево")) <> Неопределено;
	
	Если Не БезЗначенияNull
	   И Не (СохранениеЗначенияБулево И КоличествоТиповПоля = 1) Тогда
		СодержаниеВыбора = СодержаниеВыбора + "
		|КОГДА #ИмяПоляДляЗапроса ЕСТЬ NULL
		|	ТОГДА Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null)"; // @query-part-1
	КонецЕсли;
	Если КоличествоТиповПоля > 1 Тогда
		СодержаниеВыбора = СодержаниеВыбора + "
		|КОГДА #ИмяПоляДляЗапроса = НЕОПРЕДЕЛЕНО
		|	ТОГДА Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Неопределено)"; // @query-part-1
	КонецЕсли;
	Если СохранениеЗначенияБулево Тогда
		Если КоличествоТиповПоля > 1 Тогда
			СодержаниеВыбора = СодержаниеВыбора + "
			|КОГДА #ИмяПоляДляЗапроса
			|	ТОГДА Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Истина)
			|КОГДА НЕ #ИмяПоляДляЗапроса
			|	ТОГДА Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Ложь)"; // @query-part-1
		Иначе
			СодержаниеВыбора = СодержаниеВыбора + "
			|КОГДА #ИмяПоляДляЗапроса
			|	ТОГДА Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Истина)
			|ИНАЧЕ Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Ложь)"; // @query-part-1
		КонецЕсли;
	КонецЕсли;
	
	ПроверкиПоТипам = Новый СписокЗначений;
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияТиповПростых,
		"Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ТипРазрешенный)"); // @query-part-1
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияКлючейДоступа,
		"Шапка?.Значение? = ЕСТЬNULL(КлючиДоступаКОбъектам?.#КлючДоступаПользователейКОбъекту,
		|					ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null))"); // @query-part-1
	
	Если Не СвойстваПоля.ЭтоСписокЗначенийДоступаСГруппамиЗначений Тогда
		ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияГруппЗначений,
			"Шапка?.Значение? = ЕСТЬNULL(ГруппыЗначений?.ГруппаЗначенийДоступа,
			|					ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null))"); // @query-part-1
	ИначеЕсли СвойстваПоля.НесколькоГруппЗначений Тогда
		ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияГруппЗначений,
			"Шапка?.Значение? = ЕСТЬNULL(ГруппыЗначений?.ГруппаДоступа,
			|					ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null))"); // @query-part-1
	Иначе
		ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияГруппЗначений,
			"Шапка?.Значение? = ЕСТЬNULL(ТекущийСписок.ГруппаДоступа,
			|					ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.Null))"); // @query-part-1
	КонецЕсли;
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияЗначений,
		"Шапка?.Значение? = #ИмяПоляДляЗапроса", , СохранениеЗначенияБулево);
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияПустойСсылки,
		"Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ПустаяСсылкаЛюбогоТипа)", // @query-part-1
		"ЭтоПроверкаПустойСсылки");
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияТиповКонфигурации,
		"Шапка?.Значение? = ЕСТЬNULL(ТипыКонфигурации?.Ссылка,
		|					ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовМетаданных.ПустаяСсылка))"); // @query-part-1
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияТиповРасширений,
		"Шапка?.Значение? = ЕСТЬNULL(ТипыРасширений?.Ссылка,
		|					ЗНАЧЕНИЕ(Справочник.ИдентификаторыОбъектовРасширений.ПустаяСсылка))"); // @query-part-1
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияТипаЗапрещенный,
		"Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ТипЗапрещенный)"); // @query-part-1
	
	ДобавитьПроверкуПоТипам(ПроверкиПоТипам, СвойстваПоля.ТипыСохраненияТипаРазрешенный,
		"Шапка?.Значение? = ЗНАЧЕНИЕ(Перечисление.ДополнительныеЗначенияДоступа.ТипРазрешенный)"); // @query-part-1
	
	ПроверкиПоТипам.СортироватьПоПредставлению();
	
	Для Каждого ПроверкаПоТипам Из ПроверкиПоТипам Цикл
		Если ПроверкаПоТипам.Значение.Типы.Количество() = 0 Тогда
			Продолжить;
		КонецЕсли;
		Если ПроверкиПоТипам.Индекс(ПроверкаПоТипам) < ПроверкиПоТипам.Количество() - 1 Тогда
			ПроверкаТипов = "";
			Для Каждого Тип Из ПроверкаПоТипам.Значение.Типы Цикл
				Если ЭтоПростойТип(Тип) Тогда
					ИмяТипа = Строка(Тип);
				Иначе
					ИмяТипа = Метаданные.НайтиПоТипу(Тип).ПолноеИмя();
				КонецЕсли;
				Если ПроверкаТипов <> "" Тогда
					ПроверкаТипов = ПроверкаТипов + "
					|			ИЛИ ";
				КонецЕсли;
				ШаблонПроверки = ?(ПроверкаПоТипам.Значение.Свойство("ЭтоПроверкаПустойСсылки"),
					"#ИмяПоляДляЗапроса = ЗНАЧЕНИЕ(#ИмяТипа.ПустаяСсылка)",
					"ТИПЗНАЧЕНИЯ(#ИмяПоляДляЗапроса) = ТИП(#ИмяТипа)");  // @query-part-1
				ПроверкаТипов = ПроверкаТипов + СтрЗаменить(ШаблонПроверки, "#ИмяТипа", ИмяТипа);
			КонецЦикла;
			Проверка = "
			|КОГДА #ПроверкаТипов
			|	ТОГДА #Проверка"; // @query-part-1
			Проверка = СтрЗаменить(Проверка, "#ПроверкаТипов", ПроверкаТипов);
		Иначе
			Проверка = "
			|ИНАЧЕ #Проверка"; // @query-part-1
		КонецЕсли;
		Проверка = СтрЗаменить(Проверка, "#Проверка", ПроверкаПоТипам.Значение.Проверка);
		СодержаниеВыбора = СодержаниеВыбора + Проверка;
	КонецЦикла;
	
	Возврат СтрЗаменить(СравнениеПоля, "#СодержаниеВыбора", ТекстСОтступом(СокрЛ(СодержаниеВыбора), "	"));
	
КонецФункции

// Для функции СравнениеПоля.
Процедура ДобавитьПроверкуПоТипам(ПроверкиПоТипам, ИсходныеТипы, Проверка, ДополнительноеСвойство = "", ПропуститьБулево = Ложь)
	
	Типы = Новый Массив;
	Для Каждого Тип Из ИсходныеТипы Цикл
		Если ПропуститьБулево И Тип = Тип("Булево") Тогда
			Продолжить;
		КонецЕсли;
		Типы.Добавить(Тип);
	КонецЦикла;
	
	Если Типы.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Структура = Новый Структура("Типы, Проверка", Типы, Проверка);
	ПроверкиПоТипам.Добавить(Структура, Формат(Структура.Типы.Количество(), "ЧЦ=10; ЧВН=; ЧГ="));
	
	Если ДополнительноеСвойство = "" Тогда
		Возврат;
	КонецЕсли;
	
	Структура.Вставить(ДополнительноеСвойство);
	
КонецПроцедуры

// Для функции ДобавитьПроверкуШапкиКлюча.
Процедура ЗаполнитьПсевдонимПоНомеруРеквизита(Соединения, Поле, НомерРеквизита, Псевдоним)
	
	ПсевдонимСНомером = СтрЗаменить(Псевдоним, "?", НомерРеквизита);
	
	Соединения = СтрЗаменить(Соединения, Псевдоним, ПсевдонимСНомером);
	Поле       = СтрЗаменить(Поле,       Псевдоним, ПсевдонимСНомером);
	
КонецПроцедуры

// Для функций ДобавитьПроверкуШапкиКлюча, СравнениеПоля.
Функция ТекстСОтступом(Текст, Отступ)
	
	Возврат СтрЗаменить(Текст, Символы.ПС, Символы.ПС + Отступ);
	
КонецФункции

// Для функции СравнениеПоля, ДобавитьОпорноеПоле.
Функция ЕстьПростойТип(ОписаниеТипов)
	
	Возврат ОписаниеТипов.СодержитТип(Тип("Булево"))
	    Или ОписаниеТипов.СодержитТип(Тип("Дата"))
	    Или ОписаниеТипов.СодержитТип(Тип("Строка"))
	    Или ОписаниеТипов.СодержитТип(Тип("Число"))
	    Или ОписаниеТипов.СодержитТип(Тип("УникальныйИдентификатор"))
	    Или ОписаниеТипов.СодержитТип(Тип("ХранилищеЗначения"));
	
КонецФункции

#КонецОбласти

#КонецОбласти

#Область ПреобразованиеТекстовОграниченийВСтруктуры

// См. также УправлениеДоступом.РазобранноеОграничение.
//
// Возвращаемое значение:
//  Структура:
//    * ПоляТаблиц       - см. НовыеПоляТаблиц
//    * ВнутренниеДанные - см. НовыеВнутренниеДанные
//
Функция РазобранноеОграничение(ОсновнаяТаблица, ТекстОграничения) Экспорт
	
	ВнутренниеДанные = НовыеВнутренниеДанные();
	ВнутренниеДанные.Вставить("ОсновнаяТаблица",  ОсновнаяТаблица);
	ВнутренниеДанные.Вставить("ТекстОграничения", СокрЛП(ТекстОграничения));
	
	ВнутренниеДанные.Вставить("СинтаксисЯзыка", УправлениеДоступомСлужебныйПовтИсп.СинтаксисЯзыка());
	ВнутренниеДанные.Вставить("ПоляТаблиц",     НовыеПоляТаблиц());
	ВнутренниеДанные.Вставить("Псевдонимы",     Новый Соответствие);
	
	ВнутренниеДанные.Вставить("ПоляКлючаДоступа", Новый Массив);
	
	ВнутренниеДанные.Вставить("ТаблицаНаборовСимволов", ТаблицаНаборовСимволов(ВнутренниеДанные));
	ВнутренниеДанные.Вставить("ЧастиОграничения",       ЧастиОграничения(ВнутренниеДанные));
	
	Результат = Новый Структура;
	Результат.Вставить("ВнутренниеДанные", ВнутренниеДанные);
	Результат.Вставить("ПоляТаблиц", ВнутренниеДанные.ПоляТаблиц);
	
	Возврат Результат;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ОсновнаяТаблица        - Строка - таблица, которая ограничивается.
//     * ТекстОграничения       - Строка
//     * СинтаксисЯзыка         - см. СинтаксисЯзыка
//     * ПоляТаблиц             - см. НовыеПоляТаблиц
//     * Псевдонимы             - Соответствие
//     * ПоляКлючаДоступа       - Массив из см. НовоеПолеКлючаДоступа
//     * ТаблицаНаборовСимволов - см. ТаблицаНаборовСимволов
//     * ЧастиОграничения       - см. ЧастиОграничения
//
Функция НовыеВнутренниеДанные()
	
	Возврат Новый Структура;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ОсновнаяТаблица        - Строка - таблица, которая ограничивается.
//     * ТекстОграничения       - Строка
//     * СинтаксисЯзыка         - см. СинтаксисЯзыка
//     * ПоляТаблиц             - см. НовыеПоляТаблиц
//     * Псевдонимы             - Соответствие
//     * ПоляКлючаДоступа       - Массив из см. НовоеПолеКлючаДоступа
//     * ТаблицаНаборовСимволов - см. ТаблицаНаборовСимволов
//     * ЧастиОграничения       - см. ЧастиОграничения
//     * ЭтоУсловиеСоединения   - Булево
//     * ЭтоУсловиеКогда        - Булево
//     * ЭтоЗначениеТогдаИначе  - Булево
//     * КорневойУзел           - см. ОписаниеУзла
//
Функция РасширенныеВнутренниеДанные(ВнутренниеДанные)
	
	ФиксированныйКонтекст = Новый ФиксированнаяСтруктура(ВнутренниеДанные);
	Возврат Новый Структура(ФиксированныйКонтекст);
	
КонецФункции

// Возвращаемое значение:
//   Соответствие из КлючИЗначение:
//     * Ключ     - Строка - имя коллекции объектов метаданных, например, Справочники.
//     * Значение - см. НовыйСоставКоллекции
//
Функция НовыеПоляТаблиц()
	
	Возврат Новый Соответствие;
	
КонецФункции

// Возвращаемое значение:
//   Соответствие из КлючИЗначение:
//     * Ключ     - Строка - имя таблицы (объекта метаданных) в верхнем регистре.
//     * Значение - см. НовыеСвойстваТаблицы
//
Функция НовыйСоставКоллекции()
	
	Возврат Новый Соответствие;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ТаблицаСуществует - Булево - Ложь (для заполнения Истина, если существует).
//     * ЭтоОсновнаяТаблица - Булево
//     * Источники - Массив
//     * ПервоеПоле - Неопределено
//     * Поля - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - имя реквизита в верхнем регистре, в том числе через точки,
//                                например, "ВЛАДЕЛЕЦ.ОРГАНИЗАЦИЯ", "ТОВАРЫ.НОМЕНКЛАТУРА".
//         ** Значение - см. НовыеСвойстваПоля
//     * Предопределенные - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - имя предопределенного элемента.
//         ** Значение - см. НовыеСвойстваПредопределенного
//     * Расширения - Соответствие из КлючИЗначение:
//         ** Ключ - Строка - имя третьего имени таблицы, например, имя табличной части.
//         ** Значение - см. НовыеСвойстваРасширения
//
Функция НовыеСвойстваТаблицы()
	
	Возврат Новый Структура;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ПолеСОшибкой - Число - 0 (для заполнения, если поле содержит ошибку,
//          если 1, то ошибка в имени первой части поля,
//          если 2, то ошибка в имени второй части поля, т.е. после первой точки).
//     * ВидОшибки - Строка - "НеНайдено", "ТабличнаяЧастьБезПоля",
//          "ТабличнаяЧастьПослеТочки".
//     * Коллекция - Строка - пустая строка (для заполнения, если первая часть
//           поля существует, т.е. часть поля до первой точки). Варианты: "Реквизиты",
//           "ТабличныеЧасти", "СтандартныеРеквизиты", "СтандартныеТабличныеЧасти",
//           "Измерения", "Ресурсы", "Графы", "ПризнакиУчета", "ПризнакиУчетаСубконто",
//           "РеквизитыАдресации", "СпециальныеПоля". Специальные поля - это
//           "Значение" - у таблиц "Константа.*",
//           "Регистратор" и "Период" - у таблиц "Последовательность.*",
//           "ОбъектПерерасчета", "ВидРасчета" у таблиц "РегистрРасчета.<Имя>.<ИмяПерерасчета>".
//           Поля после первой точки могут относится только к коллекциям: "Реквизиты",
//           "СтандартныеРеквизиты", "ПризнакиУчета", "РеквизитыАдресации". Для этих
//           частей имени поля не требуется уточнять коллекцию.
//     * СодержитТипы - Соответствие из КлючИЗначение:
//         ** Ключ - Строка - полное имя ссылочной таблицы в верхнем регистре.
//         ** Значение - Структура:
//              *** ИмяТипа     - Строка - имя типа, наличие которого нужно проверить.
//              *** СодержитТип - Булево - Ложь (для заполнения Истина,
//                                         если у поля последнего поля есть тип).
//     * ПервыйИсточник - Структура:
//         ** Ключ     - СтрокаТаблицыЗначений - строка-источник первого поля.
//         ** Значение - Строка - таблица
//
Функция НовыеСвойстваПоля()
	
	Возврат Новый Структура;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ИмяСуществует - Булево - Ложь (для заполнения Истина, если предопределенный есть).
//     * Источники - Массив
//
Функция НовыеСвойстваПредопределенного()
	
	Возврат Новый Структура;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ТаблицаСуществует - Булево - Ложь (для заполнения Истина, если существует).
//     * Источники - Массив
//     * ПервоеПоле - Неопределено
//     * Поля - Соответствие из КлючИЗначение:
//         ** Ключ     - Строка - имя реквизита в верхнем регистре, в том числе через точки,
//                                например, "ВЛАДЕЛЕЦ.ОРГАНИЗАЦИЯ", "ТОВАРЫ.НОМЕНКЛАТУРА".
//         ** Значение - см. НовыеСвойстваПоля
//
Функция НовыеСвойстваРасширения()
	
	Возврат Новый Структура;
	
КонецФункции


// Смотри также УправлениеДоступом.СтруктураОграничения
//
// Параметры:
//  РазобранноеОграничение - см. РазобранноеОграничение
//
// Возвращаемое значение:
//   Структура:
//     * ОписаниеОшибок           - см. ОписаниеОшибок
//     * ДополнительныеТаблицы    - Массив из см. НовоеОписаниеСоединения
//     * ПсевдонимОсновнойТаблицы - Строка - заполнено, если указаны дополнительные таблицы.
//     * ОграничениеЧтения        - см. ОписаниеУзла
//     * ОграничениеИзменения     - см. ОписаниеУзла
//
Функция СтруктураОграничения(РазобранноеОграничение) Экспорт
	
	ВнутренниеДанные = РазобранноеОграничение.ВнутренниеДанные;
	
	ОтметитьНекорректныеИменаТаблицПолейИТиповПолей(РазобранноеОграничение.ПоляТаблиц,
		ВнутренниеДанные);
	
	// Заполнение найденных ошибок.
	ОписаниеОшибок = ОписаниеОшибок();
	
	Таблица = ВнутренниеДанные.ТаблицаНаборовСимволов;
	ОтборСтрокБезОшибок = Новый Структура("ТекстОшибки", "");
	Если Таблица.Количество() <> Таблица.НайтиСтроки(ОтборСтрокБезОшибок).Количество() Тогда
		ОписаниеОшибок.ЕстьОшибки = Истина;
		ТребуетсяДополнение = Ложь;
		ДлинаНомераСтроки = СтрДлина(Формат(СтрЧислоСтрок(ВнутренниеДанные.ТекстОграничения), "ЧГ="));
		Для Каждого Строка Из Таблица Цикл
			Если Строка.ТекстОшибки = "" Тогда
				Продолжить;
			КонецЕсли;
			ДобавитьОшибку(Строка, ОписаниеОшибок, ВнутренниеДанные, ДлинаНомераСтроки);
			Если Строка.ПозицияОшибки = -1 Тогда
				ТребуетсяДополнение = Истина;
			КонецЕсли;
		КонецЦикла;
		ОписаниеОшибок.Ограничение = ПронумерованныйТекстОграниченияСОтметкамиОшибок(
			ВнутренниеДанные.ТекстОграничения, ОписаниеОшибок.Ошибки, ДлинаНомераСтроки);
		Если ТребуетсяДополнение Тогда
			ОписаниеОшибок.Дополнение = ОписаниеДопустимыхШаблонов();
		КонецЕсли;
	КонецЕсли;
	
	ЧастиОграничения = ВнутренниеДанные.ЧастиОграничения;
	
	СтруктураОграничения = Новый Структура;
	СтруктураОграничения.Вставить("ОписаниеОшибок",           ОписаниеОшибок);
	СтруктураОграничения.Вставить("ДополнительныеТаблицы",    ЧастиОграничения.ДополнительныеТаблицы);
	СтруктураОграничения.Вставить("ПсевдонимОсновнойТаблицы", ЧастиОграничения.ПсевдонимОсновнойТаблицы);
	СтруктураОграничения.Вставить("ОграничениеЧтения",        ЧастиОграничения.ОграничениеЧтения);
	СтруктураОграничения.Вставить("ОграничениеИзменения",     ЧастиОграничения.ОграничениеИзменения);
	
	// Дополнительные сведения для внутреннего использования.
	ИмяТипаТаблицы = СтрРазделить(ВнутренниеДанные.ОсновнаяТаблица, ".")[0];
	СвойстваТипаТаблиц = ВнутренниеДанные.СинтаксисЯзыка.ТипыТаблиц.ПоИменам.Получить(ВРег(ИмяТипаТаблицы));
	
	НовыеВнутренниеДанные = Новый Структура;
	НовыеВнутренниеДанные.Вставить("ПоляКлючаДоступа",   ВнутренниеДанные.ПоляКлючаДоступа);
	НовыеВнутренниеДанные.Вставить("ЭтоСсылочныйТип",    СвойстваТипаТаблиц.ЭтоСсылочныйТип);
	НовыеВнутренниеДанные.Вставить("ИмяКоллекцииТипа",   СвойстваТипаТаблиц.ИмяКоллекции);
	НовыеВнутренниеДанные.Вставить("ТипыТаблицПоИменам", ВнутренниеДанные.СинтаксисЯзыка.ТипыТаблиц.ПоИменам);
	
	СтруктураОграничения.Вставить("ВнутренниеДанные", НовыеВнутренниеДанные);
	
	Для Каждого Строка Из ВнутренниеДанные.ТаблицаНаборовСимволов Цикл
		Строка.Строки.Очистить();
		Строка.КонечнаяСтрока = Неопределено;
	КонецЦикла;
	ВнутренниеДанные.ТаблицаНаборовСимволов.Очистить();
	ВнутренниеДанные.ЧастиОграничения.Очистить();
	ВнутренниеДанные.Очистить();
	
	Возврат СтруктураОграничения;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * ЕстьОшибки  - Булево
//     * ТекстОшибок - Строка
//     * Ограничение - Строка
//     * Ошибки      - Массив из см. СвойстваОшибки
//     * Дополнение  - Строка
// 
Функция ОписаниеОшибок()
	
	ОписаниеОшибок = Новый Структура;
	ОписаниеОшибок.Вставить("ЕстьОшибки",  Ложь);
	ОписаниеОшибок.Вставить("ТекстОшибок", "");
	ОписаниеОшибок.Вставить("Ограничение", "");
	ОписаниеОшибок.Вставить("Ошибки",      Новый Массив);
	ОписаниеОшибок.Вставить("Дополнение",  "");
	
	Возврат ОписаниеОшибок;
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//    * НомерСтроки    - Число  - номер строки текста ограничения, в которой найдена ошибка.
//    * ПозицияВСтроке - Число  - позиция в строке текста ограничения, в которой найдена ошибка.
//    * ТекстОшибки    - Строка - описание ошибки.
//    * СтрокаОшибки   - Строка - строка текста ограничения, в которой найдена ошибка.
//
Функция СвойстваОшибки()
	
	Возврат Новый Структура;
	
КонецФункции

// Формирует полный текст ошибок описания ограничения доступа с дополнением,
// который можно указать, как текст для вызова исключения.
//
// Параметры:
//  ПолноеИмя               - Строка    - полное имя таблицы списка.
//  ОписаниеОшибок          - Структура - значение возвращаемое функцией СтруктураОграничения.
//  ДляВнешнихПользователей - Булево    - если передать Истина, тогда текст ошибки будет содержать
//                                        назначение ограничения для внешних пользователей.
//
// Возвращаемое значение:
//  Строка - текст для вызова исключения.
//
Функция ТекстОшибокДляВызоваИсключения(ПолноеИмя, ОписаниеОшибок, ДляВнешнихПользователей, ВМодулеМенеджера)
	
	Если Не ОписаниеОшибок.ЕстьОшибки Тогда
		Возврат "";
	КонецЕсли;
	
	Если ОписаниеОшибок.Ошибки.Количество() = 1 Тогда
		Уточнение = "";
		Если ВМодулеМенеджера = Неопределено Тогда
			Если ДляВнешнихПользователей Тогда
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибка в ограничении доступа внешних пользователей к списку ""%1"":'");
			Иначе
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибка в ограничении доступа пользователей к списку ""%1"":'");
			КонецЕсли;
		ИначеЕсли ВМодулеМенеджера Тогда
			Если ДляВнешнихПользователей Тогда
				ЗаголовокОшибки =
					НСтр("ru = 'Ошибка в ограничении доступа внешних пользователей к списку ""%1"",
					           |указанному в процедуре %2 модуля менеджера объекта метаданных:'");
			Иначе
				ЗаголовокОшибки =
					НСтр("ru = 'Ошибка в ограничении доступа пользователей к списку ""%1"",
					           |указанному в процедуре %2 модуля менеджера объекта метаданных:'");
			КонецЕсли;
			Уточнение = "ПриЗаполненииОграниченияДоступа";
		Иначе
			Если ДляВнешнихПользователей Тогда
				ЗаголовокОшибки =
					НСтр("ru = 'Ошибка в ограничении доступа внешних пользователей к списку ""%1"",
					           |указанному в процедуре %2:'");
			Иначе
				ЗаголовокОшибки =
					НСтр("ru = 'Ошибка в ограничении доступа пользователей к списку ""%1"",
					           |указанному в процедуре %2:'");
			КонецЕсли;
			Уточнение = "УправлениеДоступомПереопределяемый.ПриЗаполненииОграниченияДоступа";
		КонецЕсли;
	Иначе
		Если ВМодулеМенеджера = Неопределено Тогда
			Если ДляВнешнихПользователей Тогда
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибки в ограничении доступа внешних пользователей к списку ""%1"":'");
			Иначе
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибки в ограничении доступа пользователей к списку ""%1"":'");
			КонецЕсли;
		ИначеЕсли ВМодулеМенеджера Тогда
			Если ДляВнешнихПользователей Тогда
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибки в ограничении доступа внешних пользователей к списку ""%1"",
					           |указанному в процедуре %2 модуля менеджера объекта метаданных:'");
			Иначе
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибки в ограничении доступа пользователей к списку ""%1"",
					           |указанному в процедуре %2 модуля менеджера объекта метаданных:'");
			КонецЕсли;
			Уточнение = "ПриЗаполненииОграниченияДоступа";
		Иначе
			Если ДляВнешнихПользователей Тогда
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибки в ограничении доступа внешних пользователей к списку ""%1"",
					           |указанному в процедуре %2:'");
			Иначе
				ЗаголовокОшибки = 
					НСтр("ru = 'Ошибки в ограничении доступа пользователей к списку ""%1"",
					           |указанному в процедуре %2:'");
			КонецЕсли;
			Уточнение = "УправлениеДоступомПереопределяемый.ПриЗаполненииОграниченияДоступа";
		КонецЕсли;
	КонецЕсли;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ЗаголовокОшибки, ПолноеИмя, Уточнение);
	ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС + ОписаниеОшибок.ТекстОшибок;
	ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС + ОписаниеОшибок.Ограничение;
	
	Если ЗначениеЗаполнено(ОписаниеОшибок.Дополнение) Тогда
		ТекстОшибки = ТекстОшибки + Символы.ПС + Символы.ПС + ОписаниеОшибок.Дополнение;
	КонецЕсли;
	
	Возврат Символы.ПС + ТекстОшибки + Символы.ПС;
	
КонецФункции

// Для функции СтруктураОграничения.
Процедура ДобавитьОшибку(Строка, ОписаниеОшибок, ВнутренниеДанные, ДлинаНомераСтроки)
	
	ПозицияВТексте = Строка.Позиция;
	ПозицияОшибкиВКонцеСтроки = Ложь;
	ТекстОграничения = ВнутренниеДанные.ТекстОграничения;
	
	Если Строка.ПозицияОшибки > 0 Тогда
		ПозицияВТексте = ПозицияВТексте + Строка.ПозицияОшибки;
		Если Строка.ПозицияОшибки = СтрДлина(Строка.Символы) Тогда
			ПозицияВТексте = ПозицияВТексте - 1;
			ПозицияОшибкиВКонцеСтроки = Истина;
		КонецЕсли;
	ИначеЕсли Строка.Позиция > СтрДлина(ТекстОграничения) Тогда
		ПозицияВТексте = ПозицияВТексте - 1;
		ПозицияОшибкиВКонцеСтроки = Истина;
	КонецЕсли;
	
	Координаты = КоординатыПозицииВТексте(ТекстОграничения, ПозицияВТексте);
	Координаты.ПозицияВСтроке = Координаты.ПозицияВСтроке + ?(ПозицияОшибкиВКонцеСтроки, 1, 0);
	
	СтрокаОшибки = СтрПолучитьСтроку(ТекстОграничения, Координаты.НомерСтроки);
	СтрокаОшибки = Лев(СтрокаОшибки, Координаты.ПозицияВСтроке - 1)
		+ "<<?>>" + Сред(СтрокаОшибки, Координаты.ПозицияВСтроке);
	
	Ошибка = СвойстваОшибки();
	Ошибка.Вставить("НомерСтроки",    Координаты.НомерСтроки);
	Ошибка.Вставить("ПозицияВСтроке", Координаты.ПозицияВСтроке);
	Ошибка.Вставить("ТекстОшибки",    Строка.ТекстОшибки);
	Ошибка.Вставить("СтрокаОшибки",   СтрокаОшибки);
	
	ОписаниеОшибок.Ошибки.Добавить(Ошибка);
	
	Если ЗначениеЗаполнено(ОписаниеОшибок.ТекстОшибок) Тогда
		ОписаниеОшибок.ТекстОшибок = ОписаниеОшибок.ТекстОшибок + Символы.ПС + Символы.ПС;
	КонецЕсли;
	
	ОписаниеОшибок.ТекстОшибок = ОписаниеОшибок.ТекстОшибок
		+ "{(" + Формат(Ошибка.НомерСтроки, "ЧЦ=" + ДлинаНомераСтроки + "; ЧВН=; ЧГ=")
		+ ", " + Формат(Ошибка.ПозицияВСтроке, "ЧГ=") + ")}:"
		+ " " + Ошибка.ТекстОшибки + Символы.ПС + Ошибка.СтрокаОшибки;
	
КонецПроцедуры

// Для функции СтруктураОграничения.
Функция ПронумерованныйТекстОграниченияСОтметкамиОшибок(ТекстОграничения, Ошибки, ДлинаНомераСтроки)
	
	КоличествоСтрок = СтрЧислоСтрок(ТекстОграничения);
	СтрокиТекстаОграничений = Новый Массив;
	
	Для НомерСтроки = 1 По КоличествоСтрок Цикл
		Строка = СтрПолучитьСтроку(ТекстОграничения, НомерСтроки);
		СтрокиТекстаОграничений.Добавить(Строка);
	КонецЦикла;
	
	Индекс = Ошибки.Количество() - 1;
	Пока Индекс >= 0 Цикл
		Ошибка = Ошибки[Индекс];
		Строка = СтрокиТекстаОграничений[Ошибка.НомерСтроки - 1];
		СтрокиТекстаОграничений[Ошибка.НомерСтроки - 1] = Лев(Строка, Ошибка.ПозицияВСтроке - 1)
			+ "<<?>>" + Сред(Строка, Ошибка.ПозицияВСтроке);
		Индекс = Индекс - 1;
	КонецЦикла;
	
	Текст = "";
	НомерСтроки = 1;
	
	Для Каждого Строка Из СтрокиТекстаОграничений Цикл
		Текст = Текст + ?(Текст = "", "", Символы.ПС)
			+ ?(СтрНайти(Строка, "<<?>>") > 0, "*", " ")
			+ " " + Формат(НомерСтроки, "ЧЦ=" + ДлинаНомераСтроки + "; ЧВН=; ЧГ=") + " " + Строка;
		НомерСтроки = НомерСтроки + 1;
	КонецЦикла;
	
	Возврат Текст;
	
КонецФункции

// Для функции СтруктураОграничения.
Функция ОписаниеДопустимыхШаблонов()
	
	Если ВариантВстроенногоЯзыкаРусский() Тогда
		Шаблон1 =
		"        РазрешитьЧтениеИзменение
		|        ГДЕ ..."; // @Non-NLS
		
		Шаблон2 =
		"        РазрешитьЧтение
		|        ГДЕ ...
		|        ;
		|        РазрешитьИзменениеЕслиРазрешеноЧтение
		|        ГДЕ ..."; // @Non-NLS
		
		Шаблон3 =
		"        ПрисоединитьДополнительныеТаблицы
		|        ЭтотСписок КАК <Псевдоним>
		|        ЛЕВОЕ СОЕДИНЕНИЕ ...
		|        ;
		|        РазрешитьЧтение
		|        ГДЕ ...
		|        ;
		|        РазрешитьИзменениеЕслиРазрешеноЧтение
		|        ГДЕ ..."; // @Non-NLS
	Иначе
		Шаблон1 =
		"        AllowReadWrite
		|        WHERE ...";
		
		Шаблон2 =
		"        AllowRead
		|        WHERE ...
		|        ;
		|        AllowWriteIfAllowRead
		|        WHERE ...";
		
		Шаблон3 =
		"        AttachAdditionalTables
		|        LIST AS <Alias>
		|        LEFT/INNER JOIN ...
		|        ;
		|        AllowRead
		|        WHERE ...
		|        ;
		|        AllowWriteIfAllowRead
		|        WHERE ...";
	КонецЕсли;
	
	Описание = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Ограничение может состоять из 1-3 частей в одном из 4 вариантов:
		           |1) одинаковое ограничение чтения и изменения:
		           |%1
		           |2) разные ограничения чтения и изменения:
		           |%2
		           |3) любой из вариантов выше с дополнительными таблицами, например:
		           |%3'"),
		Шаблон1, Шаблон2, Шаблон3);
	
	Возврат Описание;
	
КонецФункции

// Для процедуры ДобавитьОшибку.
Функция КоординатыПозицииВТексте(Текст, ПозицияВТексте)
	
	Результат = Новый Структура;
	Результат.Вставить("НомерСтроки", 0);
	Результат.Вставить("ПозицияВСтроке", 0);
	
	КоличествоСтрок = СтрЧислоСтрок(Текст);
	ПозицияНачалаСтроки = 1;
	Для НомерСтроки = 1 По КоличествоСтрок Цикл
		ДлинаТекущейСтроки = СтрДлина(СтрПолучитьСтроку(Текст, НомерСтроки));
		Если ПозицияВТексте < ПозицияНачалаСтроки + ДлинаТекущейСтроки Тогда
			Прервать;
		КонецЕсли;
		ПозицияНачалаСтроки = ПозицияНачалаСтроки + ДлинаТекущейСтроки + 1;
	КонецЦикла;
	
	Результат.НомерСтроки = НомерСтроки;
	Результат.ПозицияВСтроке = ПозицияВТексте - ПозицияНачалаСтроки + 1;
	
	Возврат Результат;
	
КонецФункции

#Область ЛексическийАнализ

// Для функции РазобранноеОграничение.
// Раскладывает текст в таблицу наборов символов, в которых:
// - нет символов отступа (пробелов, табуляций, переводов строки);
// - выделены символы произвольных строк и чисел;
// - определены ключевые слова и операции;
// - проверен формат имен и чисел;
// - добавлены ошибки с описанием.
// 
// Параметры:
//  ТекстОграничения - Строка - текст ограничения доступа.
//
// Возвращаемое значение:
//  ТаблицаЗначений:
//    * Символы       - Строка - символ, пара символов или слово.
//    * Позиция       - Число  - позиция символов в тексте ограничения.
//    * Вид           - Строка - "КлючевоеСлово", "Операция", "Имя", "Разделитель",
//                               "Число", "ПроизвольнаяСтрока", "НедопустимыйСимвол", "Конец".
//    * Тип           - Строка - тип для для видов "КлючевоеСлово" и "Операция".
//    * Приоритет     - Число  - приоритет для видов "КлючевоеСлово" и "Операция".
//    * Уточнение     - Число  - числовое значение для вида "Число".
//                    - Строка - имя для вида "КлючевоеСлово", строка символов для вида "ПроизвольнаяСтрока".
//    * ЭтоРезерв     - Булево - если Истина, значит это зарезервированная операция или ключевое слово.
//    * ПозицияОшибки - Число  - позиция ошибки в тексте ограничения, если текст ошибки не пустой.
//    * ТекстОшибки   - Строка - текст ошибки, если найдена ошибка.
//
Функция ТаблицаНаборовСимволов(ВнутренниеДанные)
	
	ТаблицаНаборовСимволов = Новый ТаблицаЗначений;
	ТаблицаНаборовСимволов.Колонки.Добавить("Символы",       Новый ОписаниеТипов("Строка"));
	ТаблицаНаборовСимволов.Колонки.Добавить("Позиция",       Новый ОписаниеТипов("Число"));
	ТаблицаНаборовСимволов.Колонки.Добавить("Вид",           Новый ОписаниеТипов("Строка"));
	ТаблицаНаборовСимволов.Колонки.Добавить("Тип",           Новый ОписаниеТипов("Строка"));
	ТаблицаНаборовСимволов.Колонки.Добавить("Приоритет",     Новый ОписаниеТипов("Число"));
	ТаблицаНаборовСимволов.Колонки.Добавить("Уточнение",     Новый ОписаниеТипов("Число, Строка"));
	ТаблицаНаборовСимволов.Колонки.Добавить("ЭтоРезерв",     Новый ОписаниеТипов("Булево"));
	ТаблицаНаборовСимволов.Колонки.Добавить("ПозицияОшибки", Новый ОписаниеТипов("Число"));
	ТаблицаНаборовСимволов.Колонки.Добавить("ТекстОшибки",   Новый ОписаниеТипов("Строка"));
	
	ТекстОграничения = ВнутренниеДанные.ТекстОграничения;
	
	Если Не ЗначениеЗаполнено(ТекстОграничения) Тогда
		Возврат ТаблицаНаборовСимволов;
	КонецЕсли;
	
	ДлинаТекстаОграничения = СтрДлина(ТекстОграничения);
	
	СинтаксисЯзыка = ВнутренниеДанные.СинтаксисЯзыка;
	СимволыЯзыка = СинтаксисЯзыка.СимволыЯзыка;
	
	ВидНабораСимволов = ""; // Слово, ПроизвольнаяСтрока, Операция.
	ПозицияНабораСимволов = 0;
	НаборСимволов = Новый Массив;
	СтрокаТаблицы = Неопределено;
	
	Для НомерСимвола = 1 По ДлинаТекстаОграничения Цикл
		Символ = Сред(ТекстОграничения, НомерСимвола, 1);
		ТипСимвола = СимволыЯзыка.Получить(Символ);
		// Сначала обработка символов слов, так как они встречаются наиболее часто.
		Если ТипСимвола = "СимволСлова" И ВидНабораСимволов = "Слово" Тогда
			НаборСимволов.Добавить(Символ);
			Продолжить;
		КонецЕсли;
		// Обработка произвольной строки символов.
		Если ВидНабораСимволов = "ПроизвольнаяСтрока" Тогда
			Если ТипСимвола = "ОграничительСтроки" Тогда
				Если Сред(ТекстОграничения, НомерСимвола + 1, 1) <> Символ Тогда
					СтрокаТаблицы.Уточнение = СтрСоединить(НаборСимволов);
					СтрокаТаблицы.Позиция = ПозицияНабораСимволов;
					НаборСимволов = Новый Массив;
					ВидНабораСимволов = "";
					Продолжить;
				Иначе
					НомерСимвола = НомерСимвола + 1;
				КонецЕсли;
			КонецЕсли;
			НаборСимволов.Добавить(Символ);
			Продолжить;
		КонецЕсли;
		Если ВидНабораСимволов = "Слово" Тогда
			// Вначале цикла уже обработан случай, когда ТипСимвола = "СимволСлова",
			// для остальных типов символов слово завершено и его нужно добавить в дерево.
			ВидНабораСимволов = "";
			ДобавитьСловоВТаблицуНаборовСимволов(ТаблицаНаборовСимволов,
				НаборСимволов, ПозицияНабораСимволов, СинтаксисЯзыка);
			НаборСимволов = Новый Массив;
		КонецЕсли;
		// Обработка набора составных разделителей.
		Если ВидНабораСимволов = "Операция" Тогда
			Если ТипСимвола = "СимволОперации" Тогда
				НаборСимволов.Добавить(Символ);
				Продолжить;
			КонецЕсли;
			ВидНабораСимволов = "";
			ДобавитьОперациюВТаблицуНаборовСимволов(ТаблицаНаборовСимволов,
				НаборСимволов, ПозицияНабораСимволов, СинтаксисЯзыка);
			НаборСимволов = Новый Массив;
		КонецЕсли;
		// Обработка первого символа наборов символов.
		Если ВидНабораСимволов = "" Тогда
			Если ТипСимвола = "СимволСлова" Тогда
				ВидНабораСимволов = "Слово";
				ПозицияНабораСимволов = НомерСимвола;
				НаборСимволов.Добавить(Символ);
				Продолжить;
			КонецЕсли;
			Если ТипСимвола = "СимволОперации" Тогда
				ВидНабораСимволов = "Операция";
				ПозицияНабораСимволов = НомерСимвола;
				НаборСимволов.Добавить(Символ);
				Продолжить;
			КонецЕсли;
			Если ТипСимвола = "ОграничительСтроки" Тогда
				ВидНабораСимволов = "ПроизвольнаяСтрока";
				ПозицияНабораСимволов = НомерСимвола;
				СтрокаТаблицы = ТаблицаНаборовСимволов.Добавить();
				СтрокаТаблицы.Символы = Символ;
				СтрокаТаблицы.Вид = ВидНабораСимволов;
				Продолжить;
			КонецЕсли;
		КонецЕсли;
		// Обработка отдельных символов.
		Если ТипСимвола = "Отступ" Тогда
			Продолжить;
		КонецЕсли;
		Если ТипСимвола = "Разделитель" Тогда
			СтрокаТаблицы = ТаблицаНаборовСимволов.Добавить();
			СтрокаТаблицы.Символы = Символ;
			СтрокаТаблицы.Позиция = НомерСимвола;
			СтрокаТаблицы.Вид = "Разделитель";
			Продолжить;
		КонецЕсли;
		СтрокаТаблицы = ТаблицаНаборовСимволов.Добавить();
		СтрокаТаблицы.Символы = Символ;
		СтрокаТаблицы.Позиция = НомерСимвола;
		СтрокаТаблицы.Вид = "НедопустимыйСимвол";
		СтрокаТаблицы.ТекстОшибки   = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недопустимый символ ""%1"" с кодом %2'"), Символ, КодСимвола(Символ));
	КонецЦикла;
	
	Если ВидНабораСимволов = "Слово" Тогда
		ДобавитьСловоВТаблицуНаборовСимволов(ТаблицаНаборовСимволов,
			НаборСимволов, ПозицияНабораСимволов, СинтаксисЯзыка);
		
	ИначеЕсли ВидНабораСимволов = "Операция" Тогда
		ДобавитьОперациюВТаблицуНаборовСимволов(ТаблицаНаборовСимволов,
			НаборСимволов, ПозицияНабораСимволов, СинтаксисЯзыка);
		
	ИначеЕсли ВидНабораСимволов = "ПроизвольнаяСтрока" Тогда
		СтрокаТаблицы.Уточнение = СтрСоединить(НаборСимволов);
		СтрокаТаблицы.Позиция = ПозицияНабораСимволов;
		СтрокаТаблицы.ПозицияОшибки = НомерСимвола - ПозицияНабораСимволов;
		СтрокаТаблицы.ТекстОшибки   = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не указан символ окончания произвольной строки %1'"), СтрокаТаблицы.Символы);
	КонецЕсли;
	
	ПоследняяСтрока = ТаблицаНаборовСимволов.Добавить();
	ПоследняяСтрока.Позиция = СтрДлина(ТекстОграничения) + 1;
	ПоследняяСтрока.Вид = "Конец"; // Для установки текста ошибки недостатка описания.
	ТаблицаНаборовСимволов.Индексы.Добавить("Вид, Уточнение");
	
	Возврат ТаблицаНаборовСимволов;
	
КонецФункции

// Для функции ТаблицаНаборовСимволов.
Процедура ДобавитьСловоВТаблицуНаборовСимволов(Таблица,
			НаборСимволов, ПозицияНабораСимволов, СинтаксисЯзыка)
	
	СтрокаСимволов = СтрСоединить(НаборСимволов);
	СвойстваСлова  = СинтаксисЯзыка.СловаЯзыка.Получить(ВРег(СтрокаСимволов)); // См. СвойстваСлова
	
	НоваяСтрока = Таблица.Добавить();
	НоваяСтрока.Символы = СтрокаСимволов;
	НоваяСтрока.Позиция = ПозицияНабораСимволов;
	
	Если СвойстваСлова <> Неопределено Тогда
		НоваяСтрока.Вид       = "КлючевоеСлово";
		НоваяСтрока.Тип       = СвойстваСлова.Тип;
		НоваяСтрока.Приоритет = СвойстваСлова.Приоритет;
		НоваяСтрока.Уточнение = СвойстваСлова.Идентификатор;
		НоваяСтрока.ЭтоРезерв = СвойстваСлова.ЭтоРезерв;
		
		Если СвойстваСлова.ЭтоРезерв Тогда
			НоваяСтрока.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Ключевое слово ""%1"" не поддерживается'"), СтрокаСимволов);
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	// Слово является именем или числом.
	СимволыЦифр = СинтаксисЯзыка.СимволыЦифр;
	
	Если НаборСимволов[0] = "." Тогда
		Если НаборСимволов.Количество() > 1 Тогда
			ЭтоЧисло = Ложь;
		Иначе
			НоваяСтрока.Вид = "Имя";
			НоваяСтрока.ТекстОшибки = НСтр("ru = 'Имя не может начинаться с точки'");
			Возврат;
		КонецЕсли;
	Иначе
		ЭтоЧисло = СимволыЦифр.Получить(НаборСимволов[0]) <> Неопределено;
	КонецЕсли;
	
	Если ЭтоЧисло Тогда
		НоваяСтрока.Вид = "Число";
		НомерСимвола = 1;
		Для Каждого Символ Из НаборСимволов Цикл
			Если СимволыЦифр.Получить(Символ) = Неопределено Тогда
				НоваяСтрока.ПозицияОшибки = НомерСимвола - 1;
				НоваяСтрока.ТекстОшибки   = НСтр("ru = 'Число может состоять только из цифр'");
				Возврат;
			КонецЕсли;
			НомерСимвола = НомерСимвола + 1;
		КонецЦикла;
		СимволыЧисла = Лев(СтрокаСимволов, НомерСимвола - 1);
		Если СтрДлина(СимволыЧисла) > 16 Тогда
			НоваяСтрока.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Слишком большое число ""%1""'"), СимволыЧисла);
			Возврат;
		КонецЕсли;
		НоваяСтрока.Уточнение = Число(СимволыЧисла);
	Иначе
		НоваяСтрока.Вид = "Имя";
		ЧастиИмени = СтрРазделить(СтрокаСимволов, ".");
		ПозицияЧастиИмени = 1;
		Для Каждого ЧастьИмени Из ЧастиИмени Цикл
			Если ЧастьИмени = "" И ПозицияЧастиИмени > 1 Тогда
				НоваяСтрока.ПозицияОшибки = ПозицияЧастиИмени - 1;
				НоваяСтрока.ТекстОшибки   = НСтр("ru = 'После точки не указано имя'");
				Возврат;
			ИначеЕсли СимволыЦифр.Получить(Лев(ЧастьИмени, 1)) <> Неопределено Тогда
				НоваяСтрока.ПозицияОшибки = ПозицияЧастиИмени - 1;
				НоваяСтрока.ТекстОшибки   = НСтр("ru = 'После точки в имени не может следовать число'");
				Возврат;
			КонецЕсли;
			ПозицияЧастиИмени = ПозицияЧастиИмени + СтрДлина(ЧастьИмени) + 1;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

// Для функции ТаблицаНаборовСимволов.
Процедура ДобавитьОперациюВТаблицуНаборовСимволов(Таблица,
			НаборСимволов, ПозицияНабораСимволов, СинтаксисЯзыка)
	
	СтрокаСимволов = СтрСоединить(НаборСимволов);
	СвойстваОперации = СинтаксисЯзыка.ОперацииЯзыка.Получить(СтрокаСимволов);
	
	НоваяСтрока = Таблица.Добавить();
	НоваяСтрока.Символы   = СтрокаСимволов;
	НоваяСтрока.Позиция   = ПозицияНабораСимволов;
	НоваяСтрока.Вид       = "Операция";
	НоваяСтрока.Тип       = СвойстваОперации.Тип;
	НоваяСтрока.Приоритет = СвойстваОперации.Приоритет;
	НоваяСтрока.ЭтоРезерв = СвойстваОперации.ЭтоРезерв;
	
	Если СвойстваОперации = Неопределено Тогда
		НоваяСтрока.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Недопустимая операция ""%1""'"), СтрокаСимволов);
		
	ИначеЕсли СвойстваОперации.ЭтоРезерв Тогда
		НоваяСтрока.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Операция ""%1"" не поддерживается'"), СтрокаСимволов);
	КонецЕсли;
	
КонецПроцедуры

// Для функции РазобранноеОграничение и косвенно для многих других.
//
// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//    * СимволыЯзыка  - см. СимволыЯзыка
//    * СимволыЦифр   - см. СимволыЦифр
//    * ОперацииЯзыка - см. ОперацииЯзыка
//    * СловаЯзыка    - см. СловаЯзыка
//    * ТипыТаблиц    - см. ТипыТаблиц
//
Функция СинтаксисЯзыка() Экспорт
	
	СинтаксисЯзыка = Новый Структура;
	СинтаксисЯзыка.Вставить("СимволыЯзыка",  СимволыЯзыка());
	СинтаксисЯзыка.Вставить("СимволыЦифр",   СимволыЦифр());
	СинтаксисЯзыка.Вставить("ОперацииЯзыка", ОперацииЯзыка());
	СинтаксисЯзыка.Вставить("СловаЯзыка",    СловаЯзыка());
	СинтаксисЯзыка.Вставить("ТипыТаблиц",    ТипыТаблиц());
	
	Возврат Новый ФиксированнаяСтруктура(СинтаксисЯзыка);
	
КонецФункции

// Для функции СинтаксисЯзыка.
//
// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//    * Ключ - Строка - символ
//    * Значение - Строка - вид символа
//
Функция СимволыЯзыка()
	
	СимволыЯзыка = Новый Соответствие;
	
	Для КодСимвола = КодСимвола("А") // @Non-NLS
	 По КодСимвола("Я") Цикл // @Non-NLS
		
		СимволыЯзыка.Вставить(Символ(КодСимвола), "СимволСлова");
	КонецЦикла;
	Для КодСимвола = КодСимвола("а") // @Non-NLS
	 По КодСимвола("я") Цикл // @Non-NLS
		
		СимволыЯзыка.Вставить(Символ(КодСимвола), "СимволСлова");
	КонецЦикла;
	Для КодСимвола = КодСимвола("A") По КодСимвола("Z") Цикл
	
		СимволыЯзыка.Вставить(Символ(КодСимвола), "СимволСлова");
	КонецЦикла;
	Для КодСимвола = КодСимвола("a") По КодСимвола("z") Цикл
	
		СимволыЯзыка.Вставить(Символ(КодСимвола), "СимволСлова");
	КонецЦикла;
	
	СимволыЯзыка.Вставить("_", "СимволСлова");
	СимволыЯзыка.Вставить(".", "СимволСлова");
	
	Для КодСимвола = КодСимвола("0") По КодСимвола("9") Цикл
		СимволыЯзыка.Вставить(Символ(КодСимвола), "СимволСлова");
	КонецЦикла;
	
	СимволыЯзыка.Вставить(" ",         "Отступ");
	СимволыЯзыка.Вставить(Символы.Таб, "Отступ");
	СимволыЯзыка.Вставить(Символы.ПС,  "Отступ");
	
	СимволыЯзыка.Вставить("""", "ОграничительСтроки");
	
	СимволыЯзыка.Вставить("(", "Разделитель");
	СимволыЯзыка.Вставить(")", "Разделитель");
	СимволыЯзыка.Вставить(",", "Разделитель");
	СимволыЯзыка.Вставить(";", "Разделитель");
	СимволыЯзыка.Вставить("=", "СимволОперации");
	СимволыЯзыка.Вставить("<", "СимволОперации");
	СимволыЯзыка.Вставить(">", "СимволОперации");
	
	// Не поддерживаются.
	СимволыЯзыка.Вставить("+", "СимволОперации");
	СимволыЯзыка.Вставить("-", "СимволОперации");
	СимволыЯзыка.Вставить("*", "СимволОперации");
	СимволыЯзыка.Вставить("/", "СимволОперации");
	
	Возврат Новый ФиксированноеСоответствие(СимволыЯзыка);
	
КонецФункции

// Для функции СинтаксисЯзыка.
//
// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//    * Ключ - Строка - символ
//    * Значение - Булево - Истина
//
Функция СимволыЦифр()
	
	СимволыЦифр = Новый Соответствие;
	
	Для КодСимвола = КодСимвола("0") По КодСимвола("9") Цикл
		СимволыЦифр.Вставить(Символ(КодСимвола), Истина);
	КонецЦикла;
	
	Возврат Новый ФиксированноеСоответствие(СимволыЦифр);
	
КонецФункции

// Для функции СинтаксисЯзыка.
//
// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//    * Ключ - Строка - слово на русском и английском языках.
//    * Значение - см. СвойстваСлова
//
Функция СловаЯзыка()
	
	Слова = Новый Соответствие;
	
	ДобавитьСловоЯзыка(Слова, "ПрисоединитьДополнительныеТаблицы",     // @Non-NLS
	                                                                   "AttachAdditionalTables",      "Начало",       Ложь);
	ДобавитьСловоЯзыка(Слова, "ЭтотСписок",                            // @Non-NLS
	                                                                   "ThisList",                    "НачалоСписок", Ложь);
	ДобавитьСловоЯзыка(Слова, "РазрешитьЧтениеИзменение",              // @Non-NLS
	                                                                   "AllowReadUpdate",             "Начало",       Ложь);
	ДобавитьСловоЯзыка(Слова, "РазрешитьЧтение",                       // @Non-NLS
	                                                                   "AllowRead",                   "Начало",       Ложь);
	ДобавитьСловоЯзыка(Слова, "РазрешитьИзменениеЕслиРазрешеноЧтение", // @Non-NLS
	                                                                   "AllowUpdateIfReadingAllowed", "Начало",       Ложь);
	ДобавитьСловоЯзыка(Слова, "Где",                                   // @Non-NLS
	                                                                   "Where",                       "НачалоГде");
	
	ДобавитьСловоЯзыка(Слова, "Левое",      // @Non-NLS
	                                        "Left", "Присоединение");
	ДобавитьСловоЯзыка(Слова, "Соединение", // @Non-NLS
	                                        "Join", "Присоединение");
	ДобавитьСловоЯзыка(Слова, "По",         // @Non-NLS
	                                        "On",   "Присоединение");
	
	ДобавитьСловоЯзыка(Слова, "И",      // @Non-NLS
	                                    "And",    "Соединитель", , 2);
	ДобавитьСловоЯзыка(Слова, "Или",    // @Non-NLS
	                                    "Or",     "Соединитель", , 1);
	ДобавитьСловоЯзыка(Слова, "В",      // @Non-NLS
	                                    "In",     "Соединитель", , 5);
	ДобавитьСловоЯзыка(Слова, "Как",    // @Non-NLS
	                                    "As",     "Соединитель");
	ДобавитьСловоЯзыка(Слова, "Кроме",  // @Non-NLS
	                                    "Except", "Соединитель");
	ДобавитьСловоЯзыка(Слова, "Только", // @Non-NLS
	                                    "Only",   "Соединитель");
	ДобавитьСловоЯзыка(Слова, "Есть",   // @Non-NLS
	                                    "Is",     "Соединитель", , 7);
	ДобавитьСловоЯзыка(Слова, "Не",     // @Non-NLS
	                                    "Not",    "Оператор",    , 3);
	
	ДобавитьСловоЯзыка(Слова, "Выбор",  // @Non-NLS
	                                    "Case",   "СловоВыбора");
	ДобавитьСловоЯзыка(Слова, "Когда",  // @Non-NLS
	                                    "When",   "СловоВыбора");
	ДобавитьСловоЯзыка(Слова, "Тогда",  // @Non-NLS
	                                    "Then",   "СловоВыбора");
	ДобавитьСловоЯзыка(Слова, "Иначе",  // @Non-NLS
	                                    "Else",   "СловоВыбора");
	ДобавитьСловоЯзыка(Слова, "Конец", // @Non-NLS
	                                    "End",    "СловоВыбора");
	
	ДобавитьСловоЯзыка(Слова, "ЕстьNull",                      // @Non-NLS
	                                                           "IsNull",               "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "Выразить",                      // @Non-NLS
	                                                           "Cast",                 "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "Значение",                      // @Non-NLS
	                                                           "Value",                "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ТипЗначения",                   // @Non-NLS
	                                                           "ValueType",            "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "Тип",                           // @Non-NLS
	                                                           "Type",                 "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ЗначениеРазрешено",             // @Non-NLS
	                                                           "ValueAllowed",         "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ЧтениеОбъектаРазрешено",        // @Non-NLS
	                                                           "ObjectReadingAllowed", "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ИзменениеОбъектаРазрешено",     // @Non-NLS
	                                                           "ObjectUpdateAllowed",  "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ЧтениеСпискаРазрешено",         // @Non-NLS
	                                                           "ListReadingAllowed",   "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ИзменениеСпискаРазрешено",      // @Non-NLS
	                                                           "ListUpdateAllowed",    "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ДляВсехСтрок",                  // @Non-NLS
	                                                           "ForAllRows",           "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ДляОднойИзСтрок",               // @Non-NLS
	                                                           "ForAtLeastOneRow",     "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ЭтоАвторизованныйПользователь", // @Non-NLS
	                                                           "IsAuthorizedUser",     "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "ПравоДоступа",                  // @Non-NLS
	                                                           "AccessRight",          "Функция", Ложь);
	ДобавитьСловоЯзыка(Слова, "РольДоступна",                  // @Non-NLS
	                                                           "IsInRole",             "Функция", Ложь);
	
	ДобавитьСловоЯзыка(Слова, "ПустаяСсылка", // @Non-NLS
	                                          "EmptyRef",  "ЗначениеСравнения");
	ДобавитьСловоЯзыка(Слова, "Отключено",    // @Non-NLS
	                                          "Disabled",  "ЗначениеСравнения");
	ДобавитьСловоЯзыка(Слова, "Неопределено", // @Non-NLS
	                                          "Undefined", "ЗначениеСравнения");
	ДобавитьСловоЯзыка(Слова, "Null",         // @Non-NLS
	                                          "Null",      "ЗначениеСравнения");
	ДобавитьСловоЯзыка(Слова, "Ложь",         // @Non-NLS
	                                          "False",     "ЗначениеУточнения");
	ДобавитьСловоЯзыка(Слова, "Истина",       // @Non-NLS
	                                          "True",      "ЗначениеУточнения");
	
	ДобавитьСловоЯзыка(Слова, "Строка",       // @Non-NLS
	                                          "String",    "ИмяТипа");
	ДобавитьСловоЯзыка(Слова, "Число",        // @Non-NLS
	                                          "Number",    "ИмяТипа");
	ДобавитьСловоЯзыка(Слова, "Дата",         // @Non-NLS
	                                          "Date",      "ИмяТипа");
	ДобавитьСловоЯзыка(Слова, "Булево",       // @Non-NLS
	                                          "Boolean",   "ИмяТипа");
	
	// Неподдерживаемые, зарезервированные слова.
	ДобавитьСловоЯзыка(Слова, "Выбрать",             // @Non-NLS
	                                                 "Select",          "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Первые",              // @Non-NLS
	                                                 "Top",             "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Различные",           // @Non-NLS
	                                                 "Distinct",        "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Из",                  // @Non-NLS
	                                                 "From",            "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Внутреннее",          // @Non-NLS
	                                                 "Inner",           "Присоединение", , , Истина);
	ДобавитьСловоЯзыка(Слова, "Полное",              // @Non-NLS
	                                                 "Full",            "Присоединение", , , Истина);
	ДобавитьСловоЯзыка(Слова, "Сгруппировать",       // @Non-NLS
	                                                 "Group",           "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Имеющие",             // @Non-NLS
	                                                 "Having",          "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Упорядочить",         // @Non-NLS
	                                                 "Order",           "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Итоги",               // @Non-NLS
	                                                 "Totals",          "Неопределен",   , , Истина);
	ДобавитьСловоЯзыка(Слова, "Год",                 // @Non-NLS
	                                                 "Year",            "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Квартал",             // @Non-NLS
	                                                 "Quarter",         "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Месяц",               // @Non-NLS
	                                                 "Month",           "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "ДеньГода",            // @Non-NLS
	                                                 "DayOfYear",       "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "День",                // @Non-NLS
	                                                 "Day",             "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Неделя",              // @Non-NLS
	                                                 "Week",            "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "ДеньНедели",          // @Non-NLS
	                                                 "Weekday",         "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Час",                 // @Non-NLS
	                                                 "Hour",            "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Минута",              // @Non-NLS
	                                                 "Minute",          "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Секунда",             // @Non-NLS
	                                                 "Second",          "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "НачалоПериода",       // @Non-NLS
	                                                 "BeginOfPeriod",   "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "КонецПериода",        // @Non-NLS
	                                                 "EndOfPeriod",     "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "ДобавитьКДате",       // @Non-NLS
	                                                 "DateAdd",         "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "РазностьДат",         // @Non-NLS
	                                                 "DateDiff",        "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Сумма",               // @Non-NLS
	                                                 "Sum",             "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Минимум",             // @Non-NLS
	                                                 "Min",             "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Максимум",            // @Non-NLS
	                                                 "Max",             "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Среднее",             // @Non-NLS
	                                                 "Avg",             "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Количество",          // @Non-NLS
	                                                 "Count",           "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "Представление",       // @Non-NLS
	                                                 "Presentation",    "Функция",   Ложь, , Истина);
	ДобавитьСловоЯзыка(Слова, "ПредставлениеСсылки", // @Non-NLS
	                                                 "RefPresentation", "Функция",   Ложь, , Истина);
	
	Возврат Новый ФиксированноеСоответствие(Слова);
	
КонецФункции

// Для функции СинтаксисЯзыка.
// 
// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//    * Ключ - Строка - слово на русском и английском языках.
//    * Значение - см. СвойстваСлова
//
Функция ОперацииЯзыка()
	
	ОперацииЯзыка = Новый Соответствие;
	
	ДобавитьСловоЯзыка(ОперацииЯзыка, "=",  "=",  "Соединитель", , 4);
	ДобавитьСловоЯзыка(ОперацииЯзыка, "<>", "<>", "Соединитель", , 4);
	
	// Неподдерживаемые, зарезервированные операции.
	ДобавитьСловоЯзыка(ОперацииЯзыка, "<",  "<",  "Соединитель", , 4, Истина);
	ДобавитьСловоЯзыка(ОперацииЯзыка, "<=", "<=", "Соединитель", , 4, Истина);
	ДобавитьСловоЯзыка(ОперацииЯзыка, ">",  ">",  "Соединитель", , 4, Истина);
	ДобавитьСловоЯзыка(ОперацииЯзыка, ">=", ">=", "Соединитель", , 4, Истина);
	ДобавитьСловоЯзыка(ОперацииЯзыка, "+",  "+",  "Соединитель", , 1, Истина);
	ДобавитьСловоЯзыка(ОперацииЯзыка, "-",  "-",  "Соединитель", , 1, Истина);
	ДобавитьСловоЯзыка(ОперацииЯзыка, "*",  "*",  "Соединитель", , 6, Истина);
	ДобавитьСловоЯзыка(ОперацииЯзыка, "/",  "/",  "Соединитель", , 6, Истина);
	
	Возврат Новый ФиксированноеСоответствие(ОперацииЯзыка);
	
КонецФункции

// Для функций СловаЯзыка и ОперацииЯзыка.
Процедура ДобавитьСловоЯзыка(Слова, ЯзыкРусский, ЯзыкАнглийский, ТипСлова,
			ВерхнийРегистр = Истина, Приоритет = 0, ЭтоРезерв = Ложь)
	
	СвойстваСлова = СвойстваСлова(ЯзыкРусский,
		ЯзыкАнглийский, ТипСлова, ВерхнийРегистр, Приоритет, ЭтоРезерв);
	
	Слова.Вставить(ВРег(ЯзыкРусский),    СвойстваСлова);
	Слова.Вставить(ВРег(ЯзыкАнглийский), СвойстваСлова);
	
КонецПроцедуры

// Для процедуры ДобавитьСловоЯзыка.
//
// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//    * Идентификатор  - Строка - слово языка на языке конфигурации (русском или английском).
//    * ЯзыкРусский    - Строка - слово языка на русском языке.
//    * ЯзыкАнглийский - Строка - слово языка на английском языке.
//    * Тип            - Строка - имя типа слова.
//    * ВерхнийРегистр - Булево
//    * Приоритет      - Число
//    * ЭтоРезерв      - Булево
//
Функция СвойстваСлова(ЯзыкРусский, ЯзыкАнглийский, ТипСлова, ВерхнийРегистр, Приоритет, ЭтоРезерв)
	
	СвойстваСлова = Новый Структура;
	СвойстваСлова.Вставить("Идентификатор",  ?(КодСимвола(Лев(ТипСлова, 1)) > 122, ЯзыкРусский, ЯзыкАнглийский));
	СвойстваСлова.Вставить("ЯзыкРусский",    ЯзыкРусский);
	СвойстваСлова.Вставить("ЯзыкАнглийский", ЯзыкАнглийский);
	СвойстваСлова.Вставить("Тип",            ТипСлова);
	СвойстваСлова.Вставить("ВерхнийРегистр", ВерхнийРегистр);
	СвойстваСлова.Вставить("Приоритет",      Приоритет);
	СвойстваСлова.Вставить("ЭтоРезерв",      ЭтоРезерв);
	
	Возврат Новый ФиксированнаяСтруктура(СвойстваСлова);
	
КонецФункции

// Для функции СинтаксисЯзыка.
//
// Возвращаемое значение:
//   Структура:
//     * ПоИменам - Соответствие из КлючИЗначение:
//         ** Ключ - Строка - имя типа таблиц на русском и на английском языках.
//         ** Значение - см. СвойстваТипаТаблиц
//     * ПоКоллекциям - Соответствие из КлючИЗначение:
//         ** Ключ - Строка - имя коллекции на языке конфигурации (русском или английском).
//         ** Значение - см. СвойстваТипаТаблиц
//
Функция ТипыТаблиц()
	
	ТипыТаблиц = Новый Структура;
	ТипыТаблиц.Вставить("ПоИменам",     Новый Соответствие);
	ТипыТаблиц.Вставить("ПоКоллекциям", Новый Соответствие);
	
	// Установка имен типов таблиц.
	ДобавитьТипТаблиц(ТипыТаблиц, "ПланОбмена",             // @Non-NLS
	                                                        "ExchangePlan",               "ПланыОбмена");
	ДобавитьТипТаблиц(ТипыТаблиц, "КритерийОтбора",         // @Non-NLS
	                                                        "FilterCriterion",            "КритерииОтбора");
	ДобавитьТипТаблиц(ТипыТаблиц, "Константы",              // @Non-NLS
	                                                        "Constants",                  "");
	ДобавитьТипТаблиц(ТипыТаблиц, "Константа",              // @Non-NLS
	                                                        "Constant",                   "Константы");
	ДобавитьТипТаблиц(ТипыТаблиц, "Справочник",             // @Non-NLS
	                                                        "Catalog",                    "Справочники");
	ДобавитьТипТаблиц(ТипыТаблиц, "Последовательность",     // @Non-NLS
	                                                        "Sequence",                   "Последовательности");
	ДобавитьТипТаблиц(ТипыТаблиц, "Документ",               // @Non-NLS
	                                                        "Document",                   "Документы");
	ДобавитьТипТаблиц(ТипыТаблиц, "ЖурналДокументов",       // @Non-NLS
	                                                        "DocumentJournal",            "ЖурналыДокументов");
	ДобавитьТипТаблиц(ТипыТаблиц, "Перечисление",           // @Non-NLS
	                                                        "Enum",                       "Перечисления");
	ДобавитьТипТаблиц(ТипыТаблиц, "ПланВидовХарактеристик", // @Non-NLS
	                                                        "ChartOfCharacteristicTypes", "ПланыВидовХарактеристик");
	ДобавитьТипТаблиц(ТипыТаблиц, "ПланСчетов",             // @Non-NLS
	                                                        "ChartOfAccounts",            "ПланыСчетов");
	ДобавитьТипТаблиц(ТипыТаблиц, "ПланВидовРасчета",       // @Non-NLS
	                                                        "ChartOfCalculationTypes",    "ПланыВидовРасчета");
	ДобавитьТипТаблиц(ТипыТаблиц, "РегистрСведений",        // @Non-NLS
	                                                        "InformationRegister",        "РегистрыСведений");
	ДобавитьТипТаблиц(ТипыТаблиц, "РегистрНакопления",      // @Non-NLS
	                                                        "AccumulationRegister",       "РегистрыНакопления");
	ДобавитьТипТаблиц(ТипыТаблиц, "РегистрБухгалтерии",     // @Non-NLS
	                                                        "AccountingRegister",         "РегистрыБухгалтерии");
	ДобавитьТипТаблиц(ТипыТаблиц, "РегистрРасчета",         // @Non-NLS
	                                                        "CalculationRegister",        "РегистрыРасчета");
	ДобавитьТипТаблиц(ТипыТаблиц, "БизнесПроцесс",          // @Non-NLS
	                                                        "BusinessProcess",            "БизнесПроцессы");
	ДобавитьТипТаблиц(ТипыТаблиц, "Задача",                 // @Non-NLS
	                                                        "Task",                       "Задачи");
	
	// Установка основных свойств основных типов таблиц.
	ИменаТиповТаблиц = "ПланОбмена,Справочник,Документ,ПланВидовХарактеристик,ПланСчетов,ПланВидовРасчета,БизнесПроцесс,Задача";
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ЭтоСсылочныйТип", Истина);
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ЕстьОграничение", Истина);
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ОбщиеРеквизиты",  "Разрешены");
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Использование",   "Разрешено");
	
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Реквизиты",            "Разрешены");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "СтандартныеРеквизиты", "Разрешены");
	
	ДобавитьКоллекциюТабличныхЧастейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ТабличныеЧасти", "Разрешены");
	
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ВерсияДанных",  // @Non-NLS
	                                                                      "DataVersion",  "Запрещено");
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Представление", // @Non-NLS
	                                                                      "Presentation", "Запрещено");
	
	ДобавитьРасширениеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Изменения", // @Non-NLS
	                                                                        "Changes", "Запрещено");
	
	// Установка основных свойств регистров.
	ИменаТиповТаблиц = "РегистрСведений,РегистрРасчета";
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц,     ИменаТиповТаблиц, "ОбщиеРеквизиты",       "Разрешены");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Ресурсы",              "Разрешены");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Реквизиты",            "Разрешены");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "СтандартныеРеквизиты", "Разрешены");
	
	ИменаТиповТаблиц = "РегистрНакопления,РегистрБухгалтерии";
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц,     ИменаТиповТаблиц, "ОбщиеРеквизиты",       "Недопустимы");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Ресурсы",              "Недопустимы");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Реквизиты",            "Недопустимы");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "СтандартныеРеквизиты", "Недопустимы");
	
	ИменаТиповТаблиц = "РегистрСведений,РегистрНакопления,РегистрБухгалтерии,РегистрРасчета";
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ЕстьОграничение", Истина);
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Измерения", "Разрешены");
	
	ДобавитьРасширениеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Изменения", // @Non-NLS
	                                                                        "Changes", "Запрещено");
	
	// Установка некоторых из указанных ранее свойств для остальных типов таблиц.
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц,     "Последовательность", "ЕстьОграничение", Истина);
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, "Последовательность", "Измерения", "Разрешены");
	
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц,     "ЖурналДокументов", "ОбщиеРеквизиты",       "Разрешены");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, "ЖурналДокументов", "СтандартныеРеквизиты", "Разрешены");
	
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц,     "Перечисление", "ЭтоСсылочныйТип", Истина);
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, "Перечисление", "СтандартныеРеквизиты", "Разрешены");
	
	ИменаТиповТаблиц = "Константа,РегистрСведений";
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "Использование", "Разрешено");
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, "КритерийОтбора", "Использование", "Недопустимо");
	
	ДобавитьРасширениеТипаТаблиц(ТипыТаблиц, "Константа",       "Изменения",     // @Non-NLS-2
	                                                                             "Changes",
	                                                                             "Запрещено");
	ДобавитьРасширениеТипаТаблиц(ТипыТаблиц, "РегистрСведений", "СрезПервых",    // @Non-NLS-2
	                                                                             "SliceFirst",
	                                                                             "Недопустимо");
	ДобавитьРасширениеТипаТаблиц(ТипыТаблиц, "РегистрСведений", "СрезПоследних", // @Non-NLS-2
	                                                                             "SliceLast",
	                                                                             "Недопустимо");
	ДобавитьРасширениеТипаТаблиц(ТипыТаблиц, "БизнесПроцесс",   "Точки",         // @Non-NLS-2
	                                                                             "Points",
	                                                                             "Запрещено");
	
	// Установка специализированных свойств.
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, "ПланСчетов", "ПризнакиУчетаСубконто", "Разрешены");
	
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, "ЖурналДокументов", "Графы",              "Разрешены");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, "ПланСчетов",       "ПризнакиУчета",      "Разрешены");
	ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, "Задача",           "РеквизитыАдресации", "Разрешены");
	
	ДобавитьКоллекциюТабличныхЧастейТипаТаблиц(ТипыТаблиц, "ПланСчетов",       "СтандартныеТабличныеЧасти", "Разрешены");
	ДобавитьКоллекциюТабличныхЧастейТипаТаблиц(ТипыТаблиц, "ПланВидовРасчета", "СтандартныеТабличныеЧасти", "Разрешены");
	
	ИменаТиповТаблиц = "Справочник,Перечисление,ПланВидовХарактеристик,ПланСчетов,ПланВидовРасчета";
	УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ЕстьПредопределенные", Истина);
	
	// Уточнение стандартных полей типов таблиц.
	ИменаТиповТаблиц = "Документ,РегистрСведений,РегистрНакопления,РегистрБухгалтерии";
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "МоментВремени", // @Non-NLS
	                                                                      "PointInTime", "Недопустимо");
	
	ИменаТиповТаблиц = "Справочник,ПланВидовХарактеристик,ПланСчетов,ПланВидовРасчета";
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, "ИмяПредопределенныхДанных", // @Non-NLS
	                                                                                  "PredefinedDataName", "Запрещено");
	
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, "Константа",          "Значение",    // @Non-NLS-2
	                                                                        "Value",
	                                                                        "Разрешено");
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, "Последовательность", "Регистратор", // @Non-NLS-2
	                                                                        "Recorder", 
	                                                                        "Разрешено");
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, "Последовательность", "Период",      // @Non-NLS-2
	                                                                        "Period",
	                                                                        "Разрешено");
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, "ЖурналДокументов",   "Тип",         // @Non-NLS-2
	                                                                        "Type",
	                                                                        "Недопустимо");
	ДобавитьПолеТипаТаблиц(ТипыТаблиц, "Перечисление",       "Порядок",     // @Non-NLS-2
	                                                                        "Order",
	                                                                        "Запрещено");
	
	Возврат ТипыТаблиц;
	
КонецФункции

// Для функции ТипыТаблиц.
Процедура ДобавитьТипТаблиц(ТипыТаблиц, ЯзыкРусский, ЯзыкАнглийский, ИмяКоллекции)
	
	СвойстваТипаТаблиц = СвойстваТипаТаблиц(ЯзыкРусский, ЯзыкАнглийский, ИмяКоллекции);
	
	ТипыТаблиц.ПоИменам.Вставить(ВРег(ЯзыкРусский),    СвойстваТипаТаблиц);
	ТипыТаблиц.ПоИменам.Вставить(ВРег(ЯзыкАнглийский), СвойстваТипаТаблиц);
	
	Если ЗначениеЗаполнено(ИмяКоллекции) Тогда
		ТипыТаблиц.ПоКоллекциям.Вставить(ИмяКоллекции, СвойстваТипаТаблиц);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ДобавитьСловоЯзыка.
//
// Возвращаемое значение:
//  ФиксированнаяСтруктура:
//    * ИмяКоллекции             - Строка - имя коллекции на языке конфигурации (русском или английском).
//    * ЯзыкРусский              - Строка - имя типа таблиц на русском языке.
//    * ЯзыкАнглийский           - Строка - имя типа таблиц на английском языке.
//    * ЭтоСсылочныйТип          - Булево
//    * ЕстьОграничение          - Булево
//    * ЕстьПредопределенные     - Булево
//    * КоллекцииПолей           - Соответствие из КлючИЗначение:
//        ** Ключ - Строка - имя коллекции полей на языке конфигурации (русском или английском).
//        ** Значение - Строка - 
//             "Разрешены"   - можно использовать без ограничений.
//             "Недопустимы" - нельзя использовать в ограничении доступа платформы 1С:Предприятия.
//
//    * КоллекцииТабличныхЧастей - Соответствие из КлючИЗначение:
//        ** Ключ - Строка - имя коллекции табличных частей на языке конфигурации (русском или английском).
//        ** Значение - Строка - 
//             "Разрешены"   - можно использовать без ограничений.
//             "Недопустимы" - нельзя использовать в ограничении доступа платформы 1С:Предприятия.
//
//    * ОбщиеРеквизиты           - Строка -
//        "Разрешены"   - можно использовать без ограничений.
//        "Недопустимы" - нельзя использовать в ограничении доступа платформы 1С:Предприятия.
//        "Отсутствуют" - не существуют у объекта метаданных.
//
//    * ПризнакиУчетаСубконто    - Строка -
//        "Разрешены"   - можно использовать без ограничений.
//        "Недопустимы" - нельзя использовать в ограничении доступа платформы 1С:Предприятия.
//        "Отсутствуют" - не существуют у объекта метаданных.
//
//    * УточнениеПолей           - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - имя поля таблиц на русском и на английском языках.
//        ** Значение - см. УточнениеПоля
//
//    * УточнениеТаблиц          - Соответствие из КлючИЗначение:
//        ** Ключ     - Строка - имя расширения таблиц на русском и на английском языках.
//        ** Значение - см. УточнениеТаблиц
//
//    * Использование            - Строка -
//        "Разрешено"   - можно использовать без ограничений.
//        "Недопустимо" - нельзя использовать в ограничении доступа платформы 1С:Предприятия.
//        "Запрещено"   - запрещено присоединять, как дополнительную таблицу в ограничении доступа БСП.
//
Функция СвойстваТипаТаблиц(ЯзыкРусский, ЯзыкАнглийский, ИмяКоллекции)
	
	СвойстваТипаТаблиц = Новый Структура;
	СвойстваТипаТаблиц.Вставить("ИмяКоллекции",             ИмяКоллекции);
	СвойстваТипаТаблиц.Вставить("ЯзыкРусский",              ЯзыкРусский);
	СвойстваТипаТаблиц.Вставить("ЯзыкАнглийский",           ЯзыкАнглийский);
	СвойстваТипаТаблиц.Вставить("ЭтоСсылочныйТип",          Ложь);
	СвойстваТипаТаблиц.Вставить("ЕстьОграничение",          Ложь);
	СвойстваТипаТаблиц.Вставить("ЕстьПредопределенные",     Ложь);
	СвойстваТипаТаблиц.Вставить("КоллекцииПолей",           Новый Соответствие);
	СвойстваТипаТаблиц.Вставить("КоллекцииТабличныхЧастей", Новый Соответствие);
	СвойстваТипаТаблиц.Вставить("ОбщиеРеквизиты",           "Отсутствуют");
	СвойстваТипаТаблиц.Вставить("ПризнакиУчетаСубконто",    "Отсутствуют");
	СвойстваТипаТаблиц.Вставить("УточнениеПолей",           Новый Соответствие);
	СвойстваТипаТаблиц.Вставить("УточнениеТаблиц",          Новый Соответствие);
	СвойстваТипаТаблиц.Вставить("Использование",            "Запрещено");
	
	Возврат СвойстваТипаТаблиц;
	
КонецФункции

// Для функции ТипыТаблиц.
Процедура УстановитьСвойствоТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, Свойство, Значение)
	
	Для Каждого ИмяТипаТаблицы Из СтрРазделить(ИменаТиповТаблиц, ",", Ложь) Цикл
		СвойстваТипаТаблиц = ТипыТаблиц.ПоИменам.Получить(ВРег(ИмяТипаТаблицы));
		СвойстваТипаТаблиц[Свойство] = Значение;
	КонецЦикла;
	
КонецПроцедуры

// Для функции ТипыТаблиц.
Процедура ДобавитьКоллекциюПолейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, ИмяКоллекции, Использование)
	
	Для Каждого ИмяТипаТаблицы Из СтрРазделить(ИменаТиповТаблиц, ",", Ложь) Цикл
		СвойстваТипаТаблиц = ТипыТаблиц.ПоИменам.Получить(ВРег(ИмяТипаТаблицы));
		СвойстваТипаТаблиц.КоллекцииПолей.Вставить(ИмяКоллекции, Использование);
	КонецЦикла;
	
КонецПроцедуры

// Для функции ТипыТаблиц.
Процедура ДобавитьКоллекциюТабличныхЧастейТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, ИмяКоллекции, Использование)
	
	Для Каждого ИмяТипаТаблицы Из СтрРазделить(ИменаТиповТаблиц, ",", Ложь) Цикл
		СвойстваТипаТаблиц = ТипыТаблиц.ПоИменам.Получить(ВРег(ИмяТипаТаблицы));
		СвойстваТипаТаблиц.КоллекцииТабличныхЧастей.Вставить(ИмяКоллекции, Использование);
	КонецЦикла;
	
КонецПроцедуры

// Для функции ТипыТаблиц.
Процедура ДобавитьПолеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, ЯзыкРусский, ЯзыкАнглийский, Использование)
	
	Для Каждого ИмяТипаТаблицы Из СтрРазделить(ИменаТиповТаблиц, ",", Ложь) Цикл
		УточнениеПоля = УточнениеПоля(ЯзыкРусский, ЯзыкАнглийский, Использование);
		СвойстваТипаТаблиц = ТипыТаблиц.ПоИменам.Получить(ВРег(ИмяТипаТаблицы));
		
		СвойстваТипаТаблиц.УточнениеПолей.Вставить(ВРег(ЯзыкРусский),    УточнениеПоля);
		СвойстваТипаТаблиц.УточнениеПолей.Вставить(ВРег(ЯзыкАнглийский), УточнениеПоля);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьПолеТипаТаблиц.
//
// Возвращаемое значение:
//  Структура:
//    * ЯзыкРусский    - Строка - имя поля таблиц на русском языке.
//    * ЯзыкАнглийский - Строка - имя поля таблиц на английском языке.
//    * Использование  - Строка -
//        "Разрешено"   - можно использовать без ограничений.
//        "Недопустимо" - нельзя использовать в ограничении доступа платформы 1С:Предприятия.
//        "Запрещено"   - запрещено использовать в ограничении доступа БСП.
//
Функция УточнениеПоля(ЯзыкРусский, ЯзыкАнглийский, Использование)
	
	УточнениеПоля = Новый Структура;
	УточнениеПоля.Вставить("ЯзыкРусский",    ЯзыкРусский);
	УточнениеПоля.Вставить("ЯзыкАнглийский", ЯзыкАнглийский);
	УточнениеПоля.Вставить("Использование",  Использование);
	
	Возврат УточнениеПоля;
	
КонецФункции

// Для функции ТипыТаблиц.
Процедура ДобавитьРасширениеТипаТаблиц(ТипыТаблиц, ИменаТиповТаблиц, ЯзыкРусский, ЯзыкАнглийский, Использование)
	
	Для Каждого ИмяТипаТаблицы Из СтрРазделить(ИменаТиповТаблиц, ",", Ложь) Цикл
		УточнениеТаблиц = УточнениеТаблиц(ЯзыкРусский, ЯзыкАнглийский, Использование);
		СвойстваТипаТаблиц = ТипыТаблиц.ПоИменам.Получить(ВРег(ИмяТипаТаблицы));
		
		СвойстваТипаТаблиц.УточнениеТаблиц.Вставить(ВРег(ЯзыкРусский),    УточнениеТаблиц);
		СвойстваТипаТаблиц.УточнениеТаблиц.Вставить(ВРег(ЯзыкАнглийский), УточнениеТаблиц);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ДобавитьПолеТипаТаблиц.
//
// Возвращаемое значение:
//  Структура:
//    * ЯзыкРусский    - Строка - имя расширения таблиц на русском языке.
//    * ЯзыкАнглийский - Строка - имя расширения таблиц на английском языке.
//    * Использование  - Строка -
//        "Разрешено"   - можно использовать без ограничений.
//        "Недопустимо" - нельзя использовать в ограничении доступа платформы 1С:Предприятия.
//        "Запрещено"   - запрещено использовать в ограничении доступа БСП.
//
Функция УточнениеТаблиц(ЯзыкРусский, ЯзыкАнглийский, Использование)
	
	УточнениеТаблиц = Новый Структура;
	УточнениеТаблиц.Вставить("ЯзыкРусский",    ЯзыкРусский);
	УточнениеТаблиц.Вставить("ЯзыкАнглийский", ЯзыкАнглийский);
	УточнениеТаблиц.Вставить("Использование",  Использование);
	
	Возврат УточнениеТаблиц;
	
КонецФункции

#КонецОбласти

#Область СинтаксическийАнализ

// Для функции РазобранноеОграничение.
//
// Параметры:
//   ВнутренниеДанные - см. НовыеВнутренниеДанные
//
// Возвращаемое значение:
//   Структура:
//     * ДополнительныеТаблицы    - Массив из см. НовоеОписаниеСоединения
//     * ПсевдонимОсновнойТаблицы - Строка
//     * ОграничениеЧтения        - Структура
//     * ОграничениеИзменения     - Структура
//
Функция ЧастиОграничения(ВнутренниеДанные)
	
	ЧастиОграничения = Новый Структура;
	ЧастиОграничения.Вставить("ДополнительныеТаблицы",    Новый Массив);
	ЧастиОграничения.Вставить("ПсевдонимОсновнойТаблицы", "");
	ЧастиОграничения.Вставить("ОграничениеЧтения",        Новый Структура);
	ЧастиОграничения.Вставить("ОграничениеИзменения",     Новый Структура);
	
	ТаблицаНаборовСимволов = ВнутренниеДанные.ТаблицаНаборовСимволов;
	
	Если ТаблицаНаборовСимволов.Количество() = 0 Тогда
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	ТаблицаНаборовСимволов.Колонки.Добавить("Строки", Новый ОписаниеТипов("Массив"));
	ТаблицаНаборовСимволов.Колонки.Добавить("КонечнаяСтрока");
	
	// Разделение ограничения на основные части.
	Строки = ТаблицаНаборовСимволов.НайтиСтроки(Новый Структура("Символы, Вид", ";", "Разделитель"));
	
	ИндексыСтрокРазделителя = Новый Массив;
	Для Каждого Строка Из Строки Цикл
		ИндексыСтрокРазделителя.Добавить(ТаблицаНаборовСимволов.Индекс(Строка));
	КонецЦикла;
	ИндексыСтрокРазделителя.Добавить(ТаблицаНаборовСимволов.Количество() - 1);
	
	СвойстваЧастей = Новый Массив; // Массив из см. НовыеСвойстваЧасти
	ИндексСтроки = 0;
	Для Каждого ИндексСтрокиРазделителя Из ИндексыСтрокРазделителя Цикл
		СтрокиЧасти = Новый Массив; // Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
		Пока ИндексСтроки < ИндексСтрокиРазделителя Цикл
			СтрокаЧасти = ТаблицаНаборовСимволов[ИндексСтроки];
			Если СтрокаЧасти.Вид <> "НедопустимыйСимвол" Тогда
				СтрокиЧасти.Добавить(СтрокаЧасти);
			КонецЕсли;
			ИндексСтроки = ИндексСтроки + 1;
		КонецЦикла;
		СтрокаРазделителя = ТаблицаНаборовСимволов[ИндексСтрокиРазделителя];
		Если СтрокиЧасти.Количество() = 0 Тогда
			СтрокиЧасти.Добавить(СтрокаРазделителя);
		КонецЕсли;
		СвойстваЧасти = НовыеСвойстваЧасти();
		СвойстваЧасти.Вставить("Строки", СтрокиЧасти);
		СвойстваЧасти.Вставить("СтрокаРазделителя", СтрокаРазделителя);
		СвойстваЧастей.Добавить(СвойстваЧасти);
		ПерваяСтрокаЧасти = СтрокиЧасти[0];
		Если ПерваяСтрокаЧасти.Вид = "КлючевоеСлово"
		   И ПерваяСтрокаЧасти.Тип = "Начало" Тогда
			
			СвойстваЧасти.Вставить("Имя",           ПерваяСтрокаЧасти.Уточнение);
			СвойстваЧасти.Вставить("Представление", ПерваяСтрокаЧасти.Символы);
			
			РазобратьЧастьОграничения(СвойстваЧасти, ВнутренниеДанные);
		Иначе
			СвойстваЧасти.Вставить("Имя",           "");
			СвойстваЧасти.Вставить("Представление", "");
		КонецЕсли;
		ИндексСтроки = ИндексСтрокиРазделителя + 1;
	КонецЦикла;
	
	ДобавитьПсевдонимыПоУмолчанию(ВнутренниеДанные);
	
	// Анализ части 1.
	СвойстваЧасти1 = СвойстваЧастей[0];
	
	Если СвойстваЧасти1.Имя = "" Тогда
		УстановитьОшибкуНачалаЧасти(СвойстваЧасти1, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
			НСтр("ru = 'В начале первой части текста ограничения нет ни одного из ключевых слов
			           |""%1"", ""%2"", ""%3""'"),
			"РазрешитьЧтениеИзменение,РазрешитьЧтение,ПрисоединитьДополнительныеТаблицы"));
		Возврат ЧастиОграничения;
		
	ИначеЕсли СвойстваЧасти1.Имя = "РазрешитьИзменениеЕслиРазрешеноЧтение" Тогда
		УстановитьОшибкуНачалаЧасти(СвойстваЧасти1,
			НСтр("ru = 'В начале первой части текста ограничения найдено недопустимое ключевое слово'"));
		Возврат ЧастиОграничения;
		
	ИначеЕсли СвойстваЧастей.Количество() = 1
	        И (    СвойстваЧасти1.Имя = "ПрисоединитьДополнительныеТаблицы"
	           Или СвойстваЧасти1.Имя = "РазрешитьЧтение" ) Тогда
		
		УстановитьОшибкуНачалаЧасти(ТаблицаНаборовСимволов,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Текст ограничения не может быть только из одной части
				           |с ключевым словом ""%1""'"), СвойстваЧасти1.Представление));
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	УстановитьЧастьОграничения(ЧастиОграничения, СвойстваЧасти1);
	
	Если СвойстваЧастей.Количество() < 2 Тогда
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	// Анализ части 2.
	СвойстваЧасти2 = СвойстваЧастей[1];
	
	Если СвойстваЧасти1.Имя = "РазрешитьЧтениеИзменение" Тогда
		УстановитьОшибкуНачалаЧасти(СвойстваЧасти1.СтрокаРазделителя,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Второй части текста ограничения не должно быть, когда
				           |в первой части указано ключевое слово ""%1""'"), СвойстваЧасти1.Представление));
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	Если СвойстваЧасти2.Имя = "" Тогда
		Если СвойстваЧасти1.Имя = "РазрешитьЧтение" Тогда
			УстановитьОшибкуНачалаЧасти(СвойстваЧасти2, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
				НСтр("ru = 'В начале второй части текста ограничения нет
				           |ключевого слова ""%1""'"),
				"РазрешитьИзменениеЕслиРазрешеноЧтение"));
		Иначе // СвойстваЧасти1.Имя = "ПрисоединитьДополнительныеТаблицы".
			УстановитьОшибкуНачалаЧасти(СвойстваЧасти2, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
				НСтр("ru = 'В начале второй части текста ограничения нет ни одного из ключевых слов
				           |""%1"", ""%2""'"),
				"РазрешитьЧтениеИзменение,РазрешитьЧтение"));
		КонецЕсли;
		Возврат ЧастиОграничения;
		
	ИначеЕсли СвойстваЧасти1.Имя = "РазрешитьЧтение"
	        И СвойстваЧасти2.Имя <> "РазрешитьИзменениеЕслиРазрешеноЧтение"
	      Или СвойстваЧасти1.Имя = "ПрисоединитьДополнительныеТаблицы"
	        И СвойстваЧасти2.Имя <> "РазрешитьЧтениеИзменение"
	        И СвойстваЧасти2.Имя <> "РазрешитьЧтение" Тогда
		
		УстановитьОшибкуНачалаЧасти(СвойстваЧасти2,
			НСтр("ru = 'В начале второй части текста ограничения найдено недопустимое ключевое слово'"));
		Возврат ЧастиОграничения;
		
	ИначеЕсли СвойстваЧастей.Количество() = 2
	        И СвойстваЧасти2.Имя = "РазрешитьЧтение" Тогда
		
		УстановитьОшибкуНачалаЧасти(ТаблицаНаборовСимволов,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Текст ограничения не может быть только из двух частей, когда
				           |во второй части указано ключевое слово ""%1""'"), СвойстваЧасти2.Представление));
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	УстановитьЧастьОграничения(ЧастиОграничения, СвойстваЧасти2);
	
	Если СвойстваЧастей.Количество() < 3 Тогда
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	// Анализ части 3.
	СвойстваЧасти3 = СвойстваЧастей[2];
	
	Если СвойстваЧасти2.Имя = "РазрешитьЧтениеИзменение" Тогда
		УстановитьОшибкуНачалаЧасти(СвойстваЧасти2.СтрокаРазделителя,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Третьей части текста ограничения не должно быть, когда
				           |во второй части указано ключевое слово ""%1""'"), СвойстваЧасти2.Представление));
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	Если СвойстваЧасти3.Имя = "" Тогда
		УстановитьОшибкуНачалаЧасти(СвойстваЧасти3, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
			НСтр("ru = 'В начале третьей части текста ограничения нет
			           |ключевого слово ""%1""'"),
			"РазрешитьИзменениеЕслиРазрешеноЧтение"));
		Возврат ЧастиОграничения;
		
	ИначеЕсли СвойстваЧасти2.Имя = "РазрешитьЧтение"
	        И СвойстваЧасти3.Имя <> "РазрешитьИзменениеЕслиРазрешеноЧтение" Тогда
		
		УстановитьОшибкуНачалаЧасти(СвойстваЧасти3,
			НСтр("ru = 'В начале третьей части текста ограничения найдено недопустимое ключевое слово'"));
		Возврат ЧастиОграничения;
	КонецЕсли;
	
	УстановитьЧастьОграничения(ЧастиОграничения, СвойстваЧасти3);
	
	Возврат ЧастиОграничения;
	
КонецФункции

// Процедура:
//  Родитель - СтрокаТаблицыЗначений из см. ТаблицаНаборовСимволов
//           - Структура
//  Строка   - СтрокаТаблицыЗначений из см. ТаблицаНаборовСимволов
//  Контекст - Структура:
//              * Таблица - см. ТаблицаНаборовСимволов
//
Процедура СтрокаДобавить(Родитель, Строка, Контекст)
	
	Если ТипЗнч(Строка) = Тип("СтрокаТаблицыЗначений") Тогда
		Родитель.Строки.Добавить(Контекст.ТаблицаНаборовСимволов.Индекс(Строка));
	Иначе
		Родитель.Строки.Добавить(Строка);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  ОписаниеСтроки - Число - индекс таблицы значений.
//                 - СтрокаТаблицыЗначений из см. ТаблицаНаборовСимволов
//                 - Структура - см. ДополнительнаяСтрока
//  Контекст - Структура:
//              * Таблица - см. ТаблицаНаборовСимволов
//
// Возвращаемое значение:
//   см. ДополнительнаяСтрока
//
Функция СтрокаТаблицы(ОписаниеСтроки, Контекст)
	
	Если ТипЗнч(ОписаниеСтроки) = Тип("Число") Тогда
		Строка = Контекст.ТаблицаНаборовСимволов[ОписаниеСтроки];
	Иначе
		Строка = ОписаниеСтроки;
	КонецЕсли;
	
	Возврат Строка;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * Строки                   - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//     * СтрокаРазделителя        - СтрокаТаблицыЗначений из см. ТаблицаНаборовСимволов
//     * Имя                      - Строка
//     * Представление            - Строка
//
Функция НовыеСвойстваЧасти()
	
	Возврат Новый Структура;
	
КонецФункции

// Возвращаемое значение:
//   Структура:
//     * Таблица           - Строка
//     * Псевдоним         - Строка
//     * УсловиеСоединения - см. ОписаниеУзла
//     * ПсевдонимыТребуемыхТаблиц - Массив из Строка
//     * ПоляУсловияСоединения  - Массив из см. ПараПолейУсловияСоединения
//     * ПолеПроверкиСоединения - Строка
//     * ТекстУсловияСоединения - Строка
//
Функция НовоеОписаниеСоединения()
	
	Возврат Новый Структура;
	
КонецФункции


// Возвращаемое значение:
//   Структура:
//     * Источник - СтрокаТаблицыЗначений из см. ТаблицаНаборовСимволов
//     
//     * Узел - Строка - одна из строк "Поле", "Значение", "Константа",
//         "И", "Или", "Не", "=", "<>", "В", "ЕстьNull", "Тип", "ТипЗначения", "Выбор",
//         "ЗначениеРазрешено",      "ЭтоАвторизованныйПользователь",
//         "ЧтениеОбъектаРазрешено", "ИзменениеОбъектаРазрешено",
//         "ЧтениеСпискаРазрешено",  "ИзменениеСпискаРазрешено",
//         "ДляВсехСтрок",           "ДляОднойИзСтрок",
//         "ПравоДоступа",           "РольДоступна".
//     
//     Свойства узла Поле.
//      * Имя       - Строка - имя поля, например, "Организация" или "ОсновнаяОрганизация".
//      * Таблица   - Строка - имя таблицы этого поля (или пустая строка для основной таблицы).
//      * Псевдоним - Строка - имя псевдонима присоединяемой таблицы этого поля (или пустая строка для основной таблицы),
//                      например, "РегистрСведенийНастройки" для поля "ОсновнаяОрганизация".
//      * Выразить  - Строка - имя таблицы (если используется), например, для описания поля в виде:
//                     "ВЫРАЗИТЬ(ВЫРАЗИТЬ(Владелец КАК Справочник.Файлы).ВладелецФайла КАК Справочник.Организации).Ссылка".
//      * Вложение  - Структура - узел Поле, содержащий вложенное действие ВЫРАЗИТЬ (с или без ЕстьNull).
//                  - Неопределено - нет вложенного поля.
//      * ЕстьNull  - Структура - узел Значение Или Константа, например, для описания выражения вида
//                      "ЕстьNULL(Владелец, Значение(Справочник.Файлы.ПустаяСсылка))".
//                  - Неопределено - если ЕстьNull не используется (в том числе, когда свойство Вложение заполнено).
//      * ИмяИсточник      - СтрокаТаблицыЗначений
//                         - Неопределено
//      * ВыразитьИсточник - СтрокаТаблицыЗначений
//                         - Неопределено
//      * ЕстьNullИсточник - СтрокаТаблицыЗначений
//                         - Неопределено
//
//     Свойства узлов Значение и Тип.
//      * Имя - Строка - имя значения, например, "Справочник.Организации.Основная",
//                                               "Справочник.Организации.ПустаяСсылка",
//                       имя таблицы, например, "Справочник.Организации".
//
//     Свойства узла Константа.
//      * Значение - Булево
//                 - Число
//                 - Строка
//                 - Неопределено - Ложь, Истина, произвольное
//                     целое число до 16 разрядов или произвольная строка до 150 символов.
//
//     Свойства узлов И, Или (любой узел, кроме Значение и Константа).
//      * Аргументы - Массив из см. ОписаниеУзла
//
//     Свойства узла Не (любой узел, кроме Значение и Константа).
//       * Аргумент - см. ОписаниеУзла
//       
//       * Имя - Строка - заглушка к предыдущей строке (для определения типа в EDT)
//
//     Свойства узлов =, <> (ПервыйАргумент - узел Поле,
//                           ВторойАргумент - узел Значение, Константа, а узел Поле только для условия соединения).
//       * ПервыйАргумент - см. ОписаниеУзла
//       * ВторойАргумент - см. ОписаниеУзла
//       
//       * Имя - Строка - заглушка к предыдущей строке (для определения типа в EDT)
//
//     Свойства узла В (Искомое - узел Поле, Значения - узлы Значение и/или Константа).
//       * Искомое  - см. ОписаниеУзла
//       * Значения - Массив из см. ОписаниеУзла
//
//     Свойства узла ЕстьNull (узел Поле - выражение вида "<Поле> ЕСТЬ NULL").
//       * Аргумент - см. ОписаниеУзла 
//
//       * Имя - Строка - заглушка к предыдущей строке (для определения типа в EDT)
//       
//     Свойства узла ТипЗначения (узел Поле).
//       * Аргумент - см. ОписаниеУзла
//
//       * Имя - Строка - заглушка к предыдущей строке (для определения типа в EDT)
//       
//     Свойства узла Выбор
//               В свойстве Выбор
//                    узел Поле
//                    Неопределено - условия содержат выражение, а не узел Значение
//               В свойстве Когда
//                   Условие  - узел Значение, если свойство Выбор указано, в противном случае
//                              узлы И, Или, Не, =, <>, В (распространяется на вложенное содержимое)
//                   Значение - узел, кроме Выбор
//               В свойстве Иначе
//                   узел, кроме ВЫБОР и Значение (Поле и Константа может быть только типа Булево).
//       * Выбор - см. ОписаниеУзла
//       * Когда - Массив из Структура:
//           ** Условие  - см. ОписаниеУзла
//           ** Значение - см. ОписаниеУзла
//       * Иначе - см. ОписаниеУзла
//
//       * Имя - Строка - заглушка к предыдущей строке (для определения типа в EDT)
//       
//     Свойства узлов ЗначениеРазрешено,      ЭтоАвторизованныйПользователь,
//                    ЧтениеОбъектаРазрешено, ИзменениеОбъектаРазрешено,
//                    ЧтениеСпискаРазрешено,  ИзменениеСпискаРазрешено.
//              В свойстве Поле - узел Поле.
//       * Поле - см. ОписаниеУзла
//       * Типы - Массив из Строка - полное имя таблицы
//       * ПроверятьТипыКромеУказанных - Булево - если Истина, то все типы свойства Поле,
//                                                кроме указанных в свойстве Типы.
//       * УточненияСравнения - Соответствие из КлючИЗначение:
//           ** Ключ     - Строка - уточняемое значение "Неопределено", "Null", "ПустаяСсылка",
//                                  <полное имя таблицы>, "Число", "Строка", "Дата", "Булево".
//           ** Значение - Строка - результат "Ложь", "Истина".
//
//     Свойства узлов ДляВсехСтрок, ДляОднойИзСтрок (любой узел).
//       * Аргумент - см. ОписаниеУзла.
//
Функция ОписаниеУзла()
	
	Возврат Новый Структура;
	
КонецФункции

// Для функции ЧастиОграничения.
Процедура УстановитьЧастьОграничения(ЧастиОграничения, СвойстваЧасти)
	
	Если СвойстваЧасти.Имя = "РазрешитьЧтениеИзменение"
	 Или СвойстваЧасти.Имя = "РазрешитьЧтение" Тогда
		
		ИмяСвойства = "ОграничениеЧтения";
		
	ИначеЕсли СвойстваЧасти.Имя = "РазрешитьИзменениеЕслиРазрешеноЧтение" Тогда
		
		ИмяСвойства = "ОграничениеИзменения";
		
	Иначе // ПрисоединитьДополнительныеТаблицы.
		
		ИмяСвойства = "ДополнительныеТаблицы";
		ЧастиОграничения.ПсевдонимОсновнойТаблицы = СвойстваЧасти.ПсевдонимОсновнойТаблицы;
	КонецЕсли;
	
	ЧастиОграничения[ИмяСвойства] = СвойстваЧасти.Состав;
	
КонецПроцедуры

// Для функции ЧастиОграничения, процедур РазобратьДополнительныеТаблицы, РазобратьУсловиеОграничения.
// Параметры:
//  СвойстваЧасти - см. НовыеСвойстваЧасти
//  ТекстОшибки - Строка
//
Процедура УстановитьОшибкуНачалаЧасти(СвойстваЧасти, ТекстОшибки)
	
	Если ТипЗнч(СвойстваЧасти) = Тип("ТаблицаЗначений") Тогда
		СтрокаСОшибкой = СвойстваЧасти[СвойстваЧасти.Количество() - 1];
		
	ИначеЕсли ТипЗнч(СвойстваЧасти) = Тип("СтрокаТаблицыЗначений") Тогда
		СтрокаСОшибкой = СвойстваЧасти;
	Иначе
		СтрокаСОшибкой = СвойстваЧасти.Строки[0];
	КонецЕсли;
	
	СтрокаСОшибкой.ТекстОшибки = ТекстОшибки;
	
	// Требуется описание вариантов первых ключевых слов частей.
	СтрокаСОшибкой.ПозицияОшибки = -1;
	
КонецПроцедуры

// Для процедуры РазобратьСоединение.
//  Параметры:
//   Строки - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//   ИндексСтроки - Число
//   ТекстОшибки - Строка
//
Процедура УстановитьОшибкуВнутриЧасти(Строки, ИндексСтроки, ТекстОшибки)
	
	Если ИндексСтроки < Строки.Количество() Тогда
		СтрокаСОшибкой = Строки[ИндексСтроки];
	Иначе
		СтрокаСОшибкой = Строки[ИндексСтроки - 1];
		// Ошибка в конце слова.
		СтрокаСОшибкой.ПозицияОшибки = СтрДлина(СтрокаСОшибкой.Символы);
	КонецЕсли;
	
	СтрокаСОшибкой.ТекстОшибки = ТекстОшибки;
	
КонецПроцедуры

// Для функции ВыраженияВСкобкахВоВложениях, ВыраженияВыборКогдаТогдаВоВложениях.
Процедура УстановитьОшибкуВСтроке(Строка, ТекстОшибки, ВКонцеСлова = Ложь, НомерСлова = 1)
	
	Если ЗначениеЗаполнено(Строка.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	Если ВКонцеСлова Тогда
		Строка.ПозицияОшибки = СтрДлина(Строка.Символы);
		
	ИначеЕсли НомерСлова > 1 Тогда
		СоставИмени = СтрРазделить(Строка.Символы, ".");
		Если СоставИмени.Количество() > 1 Тогда
			Строка.ПозицияОшибки = СтрДлина(СоставИмени[0]) + 1;
		КонецЕсли;
	КонецЕсли;
	
	Строка.ТекстОшибки = ТекстОшибки;
	
КонецПроцедуры

// Для функции ЧастиОграничения.
Процедура РазобратьЧастьОграничения(СвойстваЧасти, ВнутренниеДанные)
	
	Если СвойстваЧасти.Имя = "ПрисоединитьДополнительныеТаблицы" Тогда
		СвойстваЧасти.Вставить("Состав", Новый Массив);
		СвойстваЧасти.Вставить("ПсевдонимОсновнойТаблицы", "");
		РазобратьДополнительныеТаблицы(СвойстваЧасти, ВнутренниеДанные);
	Иначе
		СвойстваЧасти.Вставить("Состав", ОписаниеУзла());
		РазобратьУсловиеОграничения(СвойстваЧасти, ВнутренниеДанные);
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьЧастьОграничения.
Процедура РазобратьДополнительныеТаблицы(СвойстваЧасти, ВнутренниеДанные)
	
	СтрокиЧасти = СвойстваЧасти.Строки; // Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
	
	Если СтрокиЧасти.Количество() < 2
	 Или СтрокиЧасти[1].Вид <> "КлючевоеСлово"
	 Или СтрокиЧасти[1].Уточнение <> "ЭтотСписок" Тогда
		УстановитьОшибкуНачалаЧасти(
			?(СтрокиЧасти.Количество() < 3, СвойстваЧасти.СтрокаРазделителя, СтрокиЧасти[1]),
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'После ключевого слова ""%1"" нет
				           |ключевого слово ""%2""'"),
				СвойстваЧасти.Представление,
				КлючевоеСловоСУчетомЯзыка("ЭтотСписок", ВнутренниеДанные)));
		Возврат;
	КонецЕсли;
	
	ИзменитьВидКлючевогоСловаСписокНаИмя(СтрокиЧасти, СтрокиЧасти[1]);
	
	Если СтрокиЧасти.Количество() < 3
	 Или СтрокиЧасти[2].Вид <> "КлючевоеСлово"
	 Или СтрокиЧасти[2].Уточнение <> "Как" Тогда
		УстановитьОшибкуНачалаЧасти(
			?(СтрокиЧасти.Количество() < 3, СвойстваЧасти.СтрокаРазделителя, СтрокиЧасти[2]),
				ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
					НСтр("ru = 'После ключевого слова ""%1"" нет ключевого слово ""%2""'"),
					"ЭтотСписок,Как"));
		Возврат;
	КонецЕсли;
	
	Если СтрокиЧасти.Количество() < 4
	 Или СтрокиЧасти[3].Вид <> "Имя" Тогда
		УстановитьОшибкуНачалаЧасти(
			?(СтрокиЧасти.Количество() < 4, СвойстваЧасти.СтрокаРазделителя, СтрокиЧасти[3]),
				ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
					НСтр("ru = 'После ключевого слова ""%1"" нет псевдонима'"),
					"Как"));
		Возврат;
	КонецЕсли;
	
	УстановитьПсевдоним(СтрокиЧасти[3], СвойстваЧасти.ПсевдонимОсновнойТаблицы, ВнутренниеДанные);
	
	// Разделение описания на группы левых соединений.
	Соединения = Новый Массив;
	ТекущееСоединение = Новый Массив; // Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
	
	Для Индекс = 4 По СтрокиЧасти.Количество()-1 Цикл
		СтрокаЧасти = СтрокиЧасти[Индекс];
		
		Если СтрокаЧасти.Вид = "КлючевоеСлово"
		   И СтрокаЧасти.Уточнение = "Левое" Тогда
			
			Если ТекущееСоединение.Количество() > 0 Тогда
				Соединения.Добавить(ТекущееСоединение);
			КонецЕсли;
			ТекущееСоединение = Новый Массив;
			ТекущееСоединение.Добавить(СтрокаЧасти);
			
			Если Индекс + 1 < СтрокиЧасти.Количество()
			   И СтрокиЧасти[Индекс + 1].Вид = "КлючевоеСлово"
			   И СтрокиЧасти[Индекс + 1].Уточнение = "Соединение" Тогда
				
				Индекс = Индекс + 1;
				ТекущееСоединение.Добавить(СтрокиЧасти[Индекс]);
			КонецЕсли;
			
			Продолжить;
		КонецЕсли;
		ТекущееСоединение.Добавить(СтрокаЧасти);
	КонецЦикла;
	
	Если ТекущееСоединение.Количество() > 0
	 Или Соединения.Количество() = 0 Тогда
		
		Соединения.Добавить(ТекущееСоединение);
	КонецЕсли;
	
	ВнутренниеДанные.Вставить("ДоступныеПсевдонимы",
		Новый Соответствие(Новый ФиксированноеСоответствие(ВнутренниеДанные.Псевдонимы)));
	
	Для Каждого Соединение Из Соединения Цикл
		// Условие разбирается универсально (по максимуму возможностей)
		// после чего устанавливаются ошибки на запрещенные возможности.
		РазобратьСоединение(Соединение, СвойстваЧасти, ВнутренниеДанные);
	КонецЦикла;
	
	// Продолжение разбора после заполнения псевдонимов всех дополнительных таблиц.
	Для Каждого ОписаниеСоединения Из СвойстваЧасти.Состав Цикл
		ВнутренниеДанные.ДоступныеПсевдонимы.Вставить(ВРег(ОписаниеСоединения.Псевдоним), Истина);
		// Допустимы только простые условия:
		// Поле1 = Поле2 [И Поле3 = Поле4] [И Поле5 = Константа].
		РазобратьПоляУсловияСоединенияИОтметитьЗапреты(ОписаниеСоединения, ВнутренниеДанные);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры РазобратьДополнительныеТаблицы.
//
// Параметры:
//    Соединение - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Процедура РазобратьСоединение(Соединение, СвойстваЧасти, ВнутренниеДанные)
	
	ОписаниеСоединения = НовоеОписаниеСоединения();
	ОписаниеСоединения.Вставить("Таблица", "");
	ОписаниеСоединения.Вставить("Псевдоним", "");
	ОписаниеСоединения.Вставить("УсловиеСоединения", Неопределено);
	
	Если Соединение[0].Вид <> "КлючевоеСлово"
	 Или Соединение[0].Уточнение <> "Левое" Тогда
		УстановитьОшибкуВнутриЧасти(Соединение, 0, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
			НСтр("ru = 'Нет ключевого слова ""%1""'"), "Левое"));
		
		Если Соединение[0].Вид <> "КлючевоеСлово"
		 Или Соединение[0].Уточнение <> "Внутреннее"
		   И Соединение[0].Уточнение <> "Полное" Тогда
			
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Если Соединение.Количество() < 2
	 Или Соединение[1].Вид <> "КлючевоеСлово"
	 Или Соединение[1].Уточнение <> "Соединение" Тогда
		УстановитьОшибкуВнутриЧасти(Соединение, 1,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'После ключевого слова ""%1"" нет ключевого слова ""%2""'"),
				Соединение[0].Символы,
				КлючевоеСловоСУчетомЯзыка("Соединение", ВнутренниеДанные)));
		Возврат;
	КонецЕсли;
	
	Если Соединение.Количество() < 3
	 Или Соединение[2].Вид <> "Имя" Тогда
		УстановитьОшибкуВнутриЧасти(Соединение, 2,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'После ключевого слова ""%1"" нет имени таблицы'"),
				Соединение[1].Символы));
		Возврат;
	КонецЕсли;
	
	УстановитьИмяТаблицы(Соединение[2], ОписаниеСоединения, ВнутренниеДанные);
	
	Если Соединение.Количество() < 4
	 Или Соединение[3].Вид <> "КлючевоеСлово"
	 Или Соединение[3].Уточнение <> "Как" Тогда
		УстановитьОшибкуВнутриЧасти(Соединение, 3, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
			НСтр("ru = 'После имени таблицы нет ключевого слова ""%1""'"), "Как"));
		Возврат;
	КонецЕсли;
	
	Если Соединение.Количество() < 5
	 Или Соединение[4].Вид <> "Имя" Тогда
		УстановитьОшибкуВнутриЧасти(Соединение, 4, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
			НСтр("ru = 'После ключевого слова ""%1"" нет псевдонима таблицы'"), "Как"));
		Возврат;
	КонецЕсли;
	
	УстановитьПсевдоним(Соединение[4], ОписаниеСоединения, ВнутренниеДанные);
	
	Если Соединение.Количество() < 6
	 Или Соединение[5].Вид <> "КлючевоеСлово"
	 Или Соединение[5].Уточнение <> "По" Тогда
		УстановитьОшибкуВнутриЧасти(Соединение, 5, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
			НСтр("ru = 'После псевдонима таблицы нет ключевого слова ""%1""'"), "По"));
		Возврат;
	КонецЕсли;
	
	Условие = Новый Массив(Новый ФиксированныйМассив(Соединение));
	Для Индекс = 0 По 5 Цикл
		Условие.Удалить(0);
	КонецЦикла;
	
	РазобратьУсловие(Условие, ОписаниеСоединения.УсловиеСоединения, ВнутренниеДанные);
	
	СвойстваЧасти.Состав.Добавить(ОписаниеСоединения);
	
КонецПроцедуры

// Для процедуры РазобратьДополнительныеТаблицы.
Процедура РазобратьПоляУсловияСоединенияИОтметитьЗапреты(ОписаниеСоединения, ВнутренниеДанные)
	
	// Отметка некорректных аргументов операций и запрещенных возможностей.
	ОбщиеУзлы = Новый Соответствие(УправлениеДоступомСлужебныйПовтИсп.УзлыДляПроверкиДоступности(
		"Поле,Значение,Константа,И,=", Ложь));
	
	ОбщиеУзлы.Вставить("Поле", Новый Структура("Выразить, Вложение, ЕстьNull", Ложь, Ложь, Ложь));
	
	ДоступныеУзлы = Новый Структура;
	ДоступныеУзлы.Вставить("Общие",          ОбщиеУзлы);
	ДоступныеУзлы.Вставить("УзлыКогда",      ОбщиеУзлы);
	ДоступныеУзлы.Вставить("УзлыТогдаИначе", ОбщиеУзлы);
	
	Контекст = РасширенныеВнутренниеДанные(ВнутренниеДанные);
	Контекст.Вставить("ЭтоУсловиеСоединения",  Истина);
	Контекст.Вставить("ЭтоУсловиеКогда",       Ложь);
	Контекст.Вставить("ЭтоЗначениеТогдаИначе", Ложь);
	Контекст.Вставить("КорневойУзел",          ОписаниеСоединения.УсловиеСоединения);
	
	ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(ОписаниеСоединения.УсловиеСоединения,
		ДоступныеУзлы, Контекст);
	
	УдалитьСвойствоИсточник(ОписаниеСоединения.УсловиеСоединения);
	
КонецПроцедуры

// Для процедур РазобратьУсловиеСоединения, РазобратьУсловиеОграничения.
//
// Параметры:
//  Список - Строка - список имен узлов через запятую.
//  ЭтоСписокИсключений - Булево - если Истина, добавить узлы, кроме указанных.
//
// Возвращаемое значение:
//  ФиксированноеСоответствие из КлючИЗначение:
//    * Ключ - Строка - имя узла
//    * Значение - Булево
//               - Структура:
//       ** Выразить - Булево
//       ** Вложение - Булево
//       ** ЕстьNull - Булево
//
Функция УзлыДляПроверкиДоступности(Список, ЭтоСписокИсключений) Экспорт
	
	ВсеУзлы = Новый Соответствие;
	ВсеУзлы.Вставить("Поле", Новый Структура("Выразить, Вложение, ЕстьNull", Истина, Истина, Истина));
	ВсеУзлы.Вставить("Значение",    Истина);
	ВсеУзлы.Вставить("Константа",   Истина);
	ВсеУзлы.Вставить("И",           Истина);
	ВсеУзлы.Вставить("Или",         Истина);
	ВсеУзлы.Вставить("Не",          Истина);
	ВсеУзлы.Вставить("=",           Истина);
	ВсеУзлы.Вставить("<>",          Истина);
	ВсеУзлы.Вставить("В",           Истина);
	ВсеУзлы.Вставить("ЕстьNull",    Истина);
	ВсеУзлы.Вставить("Тип",         Истина);
	ВсеУзлы.Вставить("ТипЗначения", Истина);
	ВсеУзлы.Вставить("Выбор",       Истина);
	ВсеУзлы.Вставить("ЗначениеРазрешено",             Истина);
	ВсеУзлы.Вставить("ЭтоАвторизованныйПользователь", Истина);
	ВсеУзлы.Вставить("ЧтениеОбъектаРазрешено",        Истина);
	ВсеУзлы.Вставить("ИзменениеОбъектаРазрешено",     Истина);
	ВсеУзлы.Вставить("ЧтениеСпискаРазрешено",         Истина);
	ВсеУзлы.Вставить("ИзменениеСпискаРазрешено",      Истина);
	ВсеУзлы.Вставить("ДляВсехСтрок",                  Истина);
	ВсеУзлы.Вставить("ДляОднойИзСтрок",               Истина);
	ВсеУзлы.Вставить("ПравоДоступа",                  Истина);
	ВсеУзлы.Вставить("РольДоступна",                  Истина);
	
	МассивУзлов = СтрРазделить(Список, ",", Ложь);
	Узлы = Новый Соответствие;
	
	Для Каждого Узел Из ВсеУзлы Цикл
		Если ЭтоСписокИсключений Тогда
			Если МассивУзлов.Найти(Узел.Ключ) = Неопределено Тогда
				Узлы.Вставить(Узел.Ключ, Узел.Значение);
			КонецЕсли;
		Иначе
			Если МассивУзлов.Найти(Узел.Ключ) <> Неопределено Тогда
				Узлы.Вставить(Узел.Ключ, Узел.Значение);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Новый ФиксированноеСоответствие(Узлы);
	
КонецФункции

// Для процедур РазобратьУсловиеСоединения, РазобратьУсловиеОграничения.
//
// Параметры:
//  Условие       - см. ОписаниеУзла
//  ДоступныеУзлы - Структура
//  Контекст      - см. РасширенныеВнутренниеДанные
//  Родитель      - см. ОписаниеУзла
//
Процедура ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие, ДоступныеУзлы, Контекст, Родитель = Неопределено)
	
	Если Не ЗначениеЗаполнено(Условие) Тогда
		Возврат;
	КонецЕсли;
	
	Если Контекст.ЭтоУсловиеКогда Тогда
		ТекущиеДоступныеУзлы = ДоступныеУзлы.УзлыКогда;
		
	ИначеЕсли Контекст.ЭтоЗначениеТогдаИначе Тогда
		ТекущиеДоступныеУзлы = ДоступныеУзлы.УзлыТогдаИначе;
	Иначе
		ТекущиеДоступныеУзлы = ДоступныеУзлы.Общие;
	КонецЕсли;
	
	ДоступностьУзла = ТекущиеДоступныеУзлы.Получить(Условие.Узел);
	
	Если ДоступностьУзла = Неопределено Тогда
		УстановитьОшибкуУзелЗапрещен(Условие.Источник, Контекст);
	КонецЕсли;
	
	Если Условие.Узел = "Поле" Тогда
		
		Если Не Контекст.ЭтоУсловиеСоединения Тогда
			ПолеКлючаДоступа = НовоеПолеКлючаДоступа();
			ПолеКлючаДоступа.Вставить("Поле",   Условие);
			ПолеКлючаДоступа.Вставить("Чтение",    Контекст.Чтение);
			ПолеКлючаДоступа.Вставить("Изменение", Контекст.Изменение);
			Контекст.ПоляКлючаДоступа.Добавить(ПолеКлючаДоступа);
		КонецЕсли;
		
		ВыделитьПсевдонимПоля(Условие, Контекст);
		
		Если Не ДоступностьУзла.Выразить
		   И Условие.Выразить <> Неопределено Тогда
			
			УстановитьОшибкуУзелЗапрещен(Условие.Источник, Контекст);
			
		ИначеЕсли Не ДоступностьУзла.Вложение
		        И Условие.Вложение <> Неопределено Тогда
		
			УстановитьОшибкуУзелЗапрещен(Условие.Вложение.Источник, Контекст);
			
		ИначеЕсли Не ДоступностьУзла.ЕстьNull
		        И Условие.ЕстьNull <> Неопределено Тогда
		
			УстановитьОшибкуУзелЗапрещен(Условие.ЕстьNullИсточник, Контекст);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "И"
	      Или Условие.Узел = "Или" Тогда
		
		Для Каждого Аргумент Из Условие.Аргументы Цикл
			ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Аргумент, ДоступныеУзлы, Контекст);
		КонецЦикла;
		
	ИначеЕсли Условие.Узел = "Не"
	      Или Условие.Узел = "ЕстьNull"
	      Или Условие.Узел = "ДляВсехСтрок"
	      Или Условие.Узел = "ДляОднойИзСтрок" Тогда
		
		// Проверка корректности параметра.
		Если Условие.Узел = "ЕстьNull"
		   И (    Условие.Аргумент = Неопределено
		      Или Условие.Аргумент.Узел <> "Поле" ) Тогда
			
			УстановитьОшибкуВСтроке(Условие.Источник,
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Операция ""%1"" допустима только после поля'"),
					Условие.Источник.Символы));
		КонецЕсли;
		ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.Аргумент, ДоступныеУзлы, Контекст);
		
	ИначеЕсли Условие.Узел = "="
	      Или Условие.Узел = "<>" Тогда
		
		СочетанияУзлов = Новый Соответствие;
		СочетанияУзлов.Вставить("Значение",  ",Поле,");
		СочетанияУзлов.Вставить("Константа", ",Поле,Константа,");
		
		Если Контекст.ЭтоУсловиеСоединения Тогда
			СочетанияУзлов.Вставить("Поле",        ",Поле,Значение,Константа,");
		Иначе
			СочетанияУзлов.Вставить("Поле",        ",Значение,Константа,");
			СочетанияУзлов.Вставить("Тип",         ",ТипЗначения,");
			СочетанияУзлов.Вставить("ТипЗначения", ",Тип,");
		КонецЕсли;
		
		СочетанияПервогоАргумента = СочетанияУзлов.Получить(Условие.ПервыйАргумент.Узел);
		СочетанияВторогоАргумента = СочетанияУзлов.Получить(Условие.ВторойАргумент.Узел);
		
		ОшибкаВПервомАргументе  = СочетанияПервогоАргумента = Неопределено;
		ОшибкаВоВторомАргументе = СочетанияВторогоАргумента = Неопределено
			Или СочетанияПервогоАргумента <> Неопределено
			  И СтрНайти(СочетанияПервогоАргумента, "," + Условие.ВторойАргумент.Узел + ",") = 0;
		
		Если ОшибкаВПервомАргументе Или ОшибкаВоВторомАргументе Тогда
			Если Контекст.ЭтоУсловиеСоединения Тогда
				ТекстОшибки =
					НСтр("ru = 'Операция ""%1"" допустима только для поля с полем, значением или константой'");
			Иначе
				ТекстОшибки =
					НСтр("ru = 'Операция ""%1"" допустима только для поля со значением или константой,
					           |а также для типа значения с типом'");
			КонецЕсли;
			Если ОшибкаВПервомАргументе Тогда
				УсловиеПервыйАргумент = Условие.ПервыйАргумент; // См. ОписаниеУзла
				УстановитьОшибкуВСтроке(УсловиеПервыйАргумент.Источник,
					СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						ТекстОшибки, Условие.Источник.Символы),
					Истина);
			КонецЕсли;
			Если ОшибкаВоВторомАргументе Тогда
				УсловиеВторойАргумент = Условие.ВторойАргумент; // См. ОписаниеУзла
				УстановитьОшибкуВСтроке(УсловиеВторойАргумент.Источник,
					СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						ТекстОшибки, Условие.Источник.Символы));
			КонецЕсли;
		КонецЕсли;
		
		ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.ПервыйАргумент, ДоступныеУзлы, Контекст, Условие);
		ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.ВторойАргумент, ДоступныеУзлы, Контекст, Условие);
		
	ИначеЕсли Условие.Узел = "В" Тогда
		ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.Искомое, ДоступныеУзлы, Контекст);
		Для Каждого Значение Из Условие.Значения Цикл
			ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Значение, ДоступныеУзлы, Контекст);
		КонецЦикла;
		
	ИначеЕсли Условие.Узел = "ТипЗначения"
	      Или Условие.Узел = "Тип" Тогда
		
		Если Родитель = Неопределено
		 Или Родитель.Узел <> "="
		   И Родитель.Узел <> "<>" Тогда
		
			УстановитьОшибкуВСтроке(Условие.Источник,
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Функция ""%1"" допустима только в операциях ""="" и ""<>""'"),
					Условие.Источник.Символы));
		КонецЕсли;
		
		Если Условие.Узел = "ТипЗначения" Тогда
			ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.Аргумент, ДоступныеУзлы, Контекст);
		КонецЕсли;
		
	ИначеЕсли Условие.Узел = "Выбор" Тогда
		Если Условие.Выбор <> Неопределено Тогда
			ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.Выбор, ДоступныеУзлы, Контекст);
		КонецЕсли;
		ФиксированныйКонтекст = Новый ФиксированнаяСтруктура(Контекст);
		КонтекстКогда = Новый Структура(ФиксированныйКонтекст);
		КонтекстКогда.ЭтоУсловиеКогда = Истина;
		КонтекстТогдаИначе = Новый Структура(ФиксированныйКонтекст);
		КонтекстТогдаИначе.ЭтоЗначениеТогдаИначе = Истина;
		
		Для Каждого Когда Из Условие.Когда Цикл
			ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Когда.Условие,  ДоступныеУзлы, КонтекстКогда);
			ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Когда.Значение, ДоступныеУзлы, КонтекстТогдаИначе);
		КонецЦикла;
		ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.Иначе, ДоступныеУзлы, КонтекстТогдаИначе);
		
	ИначеЕсли Условие.Узел = "ЗначениеРазрешено"
	      Или Условие.Узел = "ЭтоАвторизованныйПользователь"
	      Или Условие.Узел = "ЧтениеОбъектаРазрешено"
	      Или Условие.Узел = "ИзменениеОбъектаРазрешено"
	      Или Условие.Узел = "ЧтениеСпискаРазрешено"
	      Или Условие.Узел = "ИзменениеСпискаРазрешено" Тогда
		
		ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(Условие.Поле, ДоступныеУзлы, Контекст);
		ОтметитьПовторыТиповСредиПроверяемыхИУточняемых(Условие, Контекст);
		
		// Добавление типов, наличие которых нужно проверить у полей.
		Поле = Условие.Поле; // См. ОписаниеУзла
		Уточнения = Новый Соответствие;
		Для Каждого УточнениеСравнения Из Условие.УточненияСравнения Цикл
			Если ТипЗнч(УточнениеСравнения.Ключ) <> Тип("СтрокаТаблицыЗначений") Тогда
				Уточнения.Вставить(УточнениеСравнения.Ключ, УточнениеСравнения.Значение);
				Продолжить;
			КонецЕсли;
			Уточнения.Вставить(УточнениеСравнения.Ключ.Символы, УточнениеСравнения.Значение);
			ДобавитьТребуемоеПолеТаблицы(Контекст, Поле.Таблица, Поле.Имя, Поле.ИмяИсточник,
				УточнениеСравнения.Ключ.Символы, УточнениеСравнения.Ключ);
		КонецЦикла;
		
		// Удаление источников типов.
		Условие.УточненияСравнения = Уточнения;
		
		Типы = Новый Массив;
		Для Каждого Тип Из Условие.Типы Цикл
			Типы.Добавить(Тип.Символы);
		КонецЦикла;
		Условие.Типы = Типы;
	КонецЕсли;
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура:
//    * Поле   - см. ОписаниеУзла
//    * Чтение - Булево
//    * Родители - Массив из см. ОписаниеУзла
//
Функция НовоеПолеКлючаДоступа()
	
	Возврат Новый Структура;
	
КонецФункции

// Для процедуры ОтметитьНекорректныеАргументыИЗапрещенныеУзлы.
Процедура ВыделитьПсевдонимПоля(УзелПоле, Контекст)
	
	Если УзелПоле.Вложение <> Неопределено Тогда
		Вложение = УзелПоле.Вложение;
		ВыделитьПсевдонимПоля(Вложение, Контекст);
		Если ЗначениеЗаполнено(Вложение.Выразить) Тогда
			УзелПоле.Таблица = Вложение.Выразить;
		КонецЕсли;
		
	ИначеЕсли Не ЗначениеЗаполнено(УзелПоле.Имя) Тогда
		Возврат;
	Иначе
		СоставИмени = СтрРазделить(УзелПоле.Имя, ".");
		Если СоставИмени.Количество() > 1 Тогда
			Свойства = Контекст.Псевдонимы.Получить(ВРег(СоставИмени[0]));
			Если Свойства <> Неопределено Тогда
				УзелПоле.Псевдоним = Свойства.Псевдоним;
				СоставИмени.Удалить(0);
				УзелПоле.Имя = СтрСоединить(СоставИмени, ".");
				Если ЗначениеЗаполнено(Свойства.Таблица) Тогда
					УзелПоле.Таблица = Свойства.Таблица;
				КонецЕсли;
			КонецЕсли;
		КонецЕсли;
		Если Контекст.ЭтоУсловиеСоединения Тогда
			Если Не ЗначениеЗаполнено(УзелПоле.Псевдоним) Тогда
				УстановитьОшибкуВСтроке(УзелПоле.ИмяИсточник,
					НСтр("ru = 'В условии соединения перед именем поля требуется псевдоним'"));
			ИначеЕсли Контекст.ДоступныеПсевдонимы.Получить(ВРег(УзелПоле.Псевдоним)) = Неопределено Тогда
				УстановитьОшибкуВСтроке(УзелПоле.ИмяИсточник,
					НСтр("ru = 'Нельзя указывать псевдоним из следующего соединения'"));
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	Если ЗначениеЗаполнено(УзелПоле.Выразить) Тогда
		ДобавитьТребуемуюТаблицуКакСсылочныйТип(Контекст, УзелПоле.Выразить, УзелПоле.ВыразитьИсточник);
	КонецЕсли;
	
	ДобавитьТребуемоеПолеТаблицы(Контекст, УзелПоле.Таблица, УзелПоле.Имя, УзелПоле.ИмяИсточник,
		УзелПоле.Выразить, УзелПоле.ВыразитьИсточник, УзелПоле);
	
КонецПроцедуры

// Для процедур РазобратьУсловиеСоединения, РазобратьУсловиеОграничения.
Процедура УдалитьСвойствоИсточник(Условие)
	
	Если ТипЗнч(Условие) = Тип("СтрокаТаблицыЗначений") Тогда
		ТекстОшибки = НСтр("ru = 'Не все источники наборов символов удалены'");
		ВызватьИсключение ТекстОшибки;
		
	ИначеЕсли ТипЗнч(Условие) <> Тип("Структура") Тогда
		Возврат;
	КонецЕсли;
	
	Если Условие.Свойство("Источник") Тогда
		Условие.Удалить("Источник");
	КонецЕсли;
	
	Если Условие.Свойство("ИмяИсточник") Тогда
		Условие.Удалить("ИмяИсточник");
	КонецЕсли;
	
	Если Условие.Свойство("ВыразитьИсточник") Тогда
		Условие.Удалить("ВыразитьИсточник");
	КонецЕсли;
	
	Если Условие.Свойство("ЕстьNullИсточник") Тогда
		Условие.Удалить("ЕстьNullИсточник");
	КонецЕсли;
	
	Для Каждого КлючИЗначение Из Условие Цикл
		Значение = КлючИЗначение.Значение;
		
		Если ТипЗнч(Значение) = Тип("Массив") Тогда
			Для Каждого Элемент Из Значение Цикл
				УдалитьСвойствоИсточник(Элемент);
			КонецЦикла;
			
		ИначеЕсли ТипЗнч(Значение) = Тип("Соответствие") Тогда
			Для Каждого КлючИЗначение Из Значение Цикл
				УдалитьСвойствоИсточник(КлючИЗначение.Ключ);
				УдалитьСвойствоИсточник(КлючИЗначение.Значение);
			КонецЦикла;
		Иначе
			УдалитьСвойствоИсточник(Значение);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОтметитьНекорректныеАргументыИЗапрещенныеУзлы.
Процедура УстановитьОшибкуУзелЗапрещен(Строка, Контекст)
	
	Если Строка.Тип = "Функция" Тогда
		Если Контекст.ЭтоУсловиеСоединения Тогда
			ШаблонОшибки = НСтр("ru = 'Функция ""%1"" запрещена в условии соединения'");
			
		ИначеЕсли Контекст.ЭтоУсловиеКогда Тогда
			ШаблонОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Функция ""%3"" запрещена в условии ограничения в операции ""%1"" в предложении ""%2""'"),
				"Выбор,Когда", Строка.Символы);
			
		ИначеЕсли Контекст.ЭтоЗначениеТогдаИначе Тогда
			ШаблонОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Функция ""%4"" запрещена в условии ограничения в операции ""%1"" в предложениях ""%2"" и ""%3""'"),
				"Выбор,Тогда,Иначе", Строка.Символы);
		Иначе
			ШаблонОшибки = НСтр("ru = 'Функция ""%1"" запрещена в условии ограничения'");
		КонецЕсли;
	Иначе
		Если Контекст.ЭтоУсловиеСоединения Тогда
			ШаблонОшибки = НСтр("ru = 'Операция ""%1"" запрещена в условии соединения'");
			
		ИначеЕсли Контекст.ЭтоУсловиеКогда Тогда
			ШаблонОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Операция ""%3"" запрещена в условии ограничения в операции ""%1"" в предложении ""%2""'"),
				"Выбор,Когда", Строка.Символы);
			
		ИначеЕсли Контекст.ЭтоЗначениеТогдаИначе Тогда
			ШаблонОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Операция ""%4"" запрещена в условии ограничения в операции ""%1"" в предложениях ""%2"" и ""%3""'"),
				"Выбор,Тогда,Иначе", Строка.Символы);
		Иначе
			ШаблонОшибки = НСтр("ru = 'Операция ""%1"" запрещена в условии ограничения'");
		КонецЕсли;
	КонецЕсли;
	
	УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		ШаблонОшибки, Строка.Символы));
	
КонецПроцедуры

// Для процедуры ОтметитьНекорректныеАргументыИЗапрещенныеУзлы.
Процедура ОтметитьПовторыТиповСредиПроверяемыхИУточняемых(Узел, Контекст)
	
	ТипыВСписке = Новый Соответствие;
	
	Для Каждого ТипВСписке Из Узел.Типы Цикл
		Если ТипыВСписке.Получить(ВРег(ТипВСписке.Символы)) = Неопределено Тогда
			ТипыВСписке.Вставить(ВРег(ТипВСписке.Символы), Истина);
		Иначе
			УстановитьОшибкуВСтроке(ТипВСписке, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Тип ""%1"" уже указан'"), ТипВСписке.Символы));
		КонецЕсли;
	КонецЦикла;
	
	УточняемыеТипы = Новый Соответствие;
	
	Для Каждого УточнениеСравнения Из Узел.УточненияСравнения Цикл
		Если ТипЗнч(УточнениеСравнения.Ключ) <> Тип("СтрокаТаблицыЗначений") Тогда
			Продолжить;
		КонецЕсли;
		ИсточникТипа = УточнениеСравнения.Ключ;
		
		Если Не Узел.ПроверятьТипыКромеУказанных
		   И ТипыВСписке.Получить(ВРег(ИсточникТипа.Символы)) <> Неопределено Тогда
			
			УстановитьОшибкуВСтроке(ИсточникТипа, ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Тип ""%2"" уже указан среди типов ключевого слова ""%1""'"),
				"Только",
				ИсточникТипа.Символы));
			
		ИначеЕсли Узел.ПроверятьТипыКромеУказанных
		        И ТипыВСписке.Получить(ВРег(ИсточникТипа.Символы)) = Неопределено Тогда
			
			УстановитьОшибкуВСтроке(ИсточникТипа, ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Тип ""%2"" не указан среди типов ключевого слова ""%1""'"),
				"Кроме",
				ИсточникТипа.Символы));
			
		ИначеЕсли УточняемыеТипы.Получить(ВРег(ИсточникТипа.Символы)) <> Неопределено Тогда
			УстановитьОшибкуВСтроке(ИсточникТипа, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Уточнение типа ""%1"" уже указано'"), ИсточникТипа.Символы));
		Иначе
			УточняемыеТипы.Вставить(ВРег(ИсточникТипа.Символы), Истина);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры РазобратьЧастьОграничения.
//
// Параметры:
//  СвойстваЧасти - см. НовыеСвойстваЧасти
//  ВнутренниеДанные - см. НовыеВнутренниеДанные
//
Процедура РазобратьУсловиеОграничения(СвойстваЧасти, ВнутренниеДанные)
	
	СтрокиЧасти = СвойстваЧасти.Строки;
	ИзменитьВидКлючевогоСловаСписокНаИмя(СтрокиЧасти);
	
	Если СтрокиЧасти.Количество() < 2
	 Или СтрокиЧасти[1].Вид <> "КлючевоеСлово"
	 Или СтрокиЧасти[1].Уточнение <> "Где" Тогда
		УстановитьОшибкуНачалаЧасти(
			?(СтрокиЧасти.Количество() < 2, СвойстваЧасти.СтрокаРазделителя, СтрокиЧасти[1]),
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'После ключевого слова ""%1"" нет
					           |ключевого слова ""%2""'"),
					СвойстваЧасти.Представление,
					КлючевоеСловоСУчетомЯзыка("Где", ВнутренниеДанные)));
		Возврат;
	КонецЕсли;
	
	Условие = Новый Массив(Новый ФиксированныйМассив(СтрокиЧасти));
	Для Индекс = 0 По 1 Цикл
		Условие.Удалить(0);
	КонецЦикла;
	
	РазобратьУсловие(Условие, СвойстваЧасти.Состав, ВнутренниеДанные);
	
	// Отметка некорректных параметров операций и неподдерживаемого функционала.
	ОбщиеУзлы = УправлениеДоступомСлужебныйПовтИсп.УзлыДляПроверкиДоступности("", Истина);
	
	УзлыКогда = УправлениеДоступомСлужебныйПовтИсп.УзлыДляПроверкиДоступности(
		"Поле,Значение,Константа,И,Или,Не,=,<>,В,ЕстьNull,Тип,ТипЗначения", Ложь);
	
	УзлыТогдаИначе = УправлениеДоступомСлужебныйПовтИсп.УзлыДляПроверкиДоступности(
		"Выбор,ДляВсехСтрок,ДляОднойИзСтрок", Истина);
	
	ДоступныеУзлы = Новый Структура;
	ДоступныеУзлы.Вставить("Общие",          ОбщиеУзлы);
	ДоступныеУзлы.Вставить("УзлыКогда",      УзлыКогда);
	ДоступныеУзлы.Вставить("УзлыТогдаИначе", УзлыТогдаИначе);
	
	ФиксированныйКонтекст = Новый ФиксированнаяСтруктура(ВнутренниеДанные);
	Контекст = Новый Структура(ФиксированныйКонтекст);
	Контекст.Вставить("ЭтоУсловиеСоединения",  Ложь);
	Контекст.Вставить("ЭтоУсловиеКогда",       Ложь);
	Контекст.Вставить("ЭтоЗначениеТогдаИначе", Ложь);
	Контекст.Вставить("КорневойУзел",          СвойстваЧасти.Состав);
	Контекст.Вставить("Чтение",
		    СвойстваЧасти.Имя = "РазрешитьЧтениеИзменение"
		Или СвойстваЧасти.Имя = "РазрешитьЧтение");
	Контекст.Вставить("Изменение",
		    СвойстваЧасти.Имя = "РазрешитьЧтениеИзменение"
		Или СвойстваЧасти.Имя = "РазрешитьИзменениеЕслиРазрешеноЧтение");
	
	ДобавитьПсевдонимыПоУмолчанию(Контекст);
	
	ОтметитьНекорректныеАргументыИЗапрещенныеУзлы(СвойстваЧасти.Состав, ДоступныеУзлы, Контекст);
	
	УдалитьСвойствоИсточник(СвойстваЧасти.Состав);
	
КонецПроцедуры

// Для процедур ЧастиОграничения, РазобратьУсловиеОграничения.
Процедура ДобавитьПсевдонимыПоУмолчанию(Контекст);
	
	Если Контекст.Псевдонимы.Количество() > 0 Тогда
		Возврат;
	КонецЕсли;
	
	Контекст.Псевдонимы.Вставить(ВРег("ЭтотСписок"), Новый Структура("Псевдоним, Таблица", "ЭтотСписок"));
	Контекст.Псевдонимы.Вставить(ВРег("ThisList"),   Новый Структура("Псевдоним, Таблица", "ThisList"));
	
КонецПроцедуры

// Для процедур РазобратьУсловиеСоединения, РазобратьУсловиеОграничения.
//
// Параметры:
//  Условие          - Массив из СтрокаТаблицыЗначений
//  Состав           - см. ОписаниеУзла
//  ВнутренниеДанные - см. НовыеВнутренниеДанные
//
Процедура РазобратьУсловие(Условие, Состав, ВнутренниеДанные)
	
	ВыраженияВСкобкахВоВложениях = ВыраженияВСкобкахВоВложениях(Условие, ВнутренниеДанные);
	
	ФункцииСВыражениямиВСкобках = ФункцииСВыражениямиВСкобках(
		ВыраженияВСкобкахВоВложениях, ВнутренниеДанные);
	
	ВыраженияВыборКогдаТогдаВоВложениях = ВыраженияВыборКогдаТогдаВоВложениях(
		ФункцииСВыражениямиВСкобках, ВнутренниеДанные);
	
	Условие = ВыраженияВыборКогдаТогдаВоВложениях;
	
	РазобратьВыражение(Условие, Состав, ВнутренниеДанные, Ложь);
	
	ОбъединитьВложенныеЛогическиеОперации(Состав);
	
КонецПроцедуры

// Для процедуры РазобратьУсловие.
Процедура ОбъединитьВложенныеЛогическиеОперации(Состав)
	
	Если Состав = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если Состав.Узел = "И"
	 Или Состав.Узел = "Или" Тогда
		
		Индекс = Состав.Аргументы.Количество() - 1;
		Пока Индекс >= 0 Цикл
			
			Аргумент = Состав.Аргументы[Индекс];
			ОбъединитьВложенныеЛогическиеОперации(Аргумент);
			
			Если Аргумент.Узел = Состав.Узел Тогда
				Состав.Аргументы.Удалить(Индекс);
				ВложенныйИндекс = Аргумент.Аргументы.Количество() - 1;
				Пока ВложенныйИндекс >= 0 Цикл
					Состав.Аргументы.Вставить(Индекс, Аргумент.Аргументы[ВложенныйИндекс]);
					ВложенныйИндекс = ВложенныйИндекс - 1;
				КонецЦикла;
			КонецЕсли;
			Индекс = Индекс - 1;
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры


// Для процедур РазобратьУсловие, РазобратьФункцию, РазобратьВыбор.
//
// Параметры:
//    Условие - Массив из см. СтрокаТаблицы.ОписаниеСтроки
//
Процедура РазобратьВыражение(Условие, Состав, ТекущийКонтекст, ВложенноеВыражение = Истина)
	
	ФиксированныйКонтекст = Новый ФиксированнаяСтруктура(ТекущийКонтекст);
	Контекст = Новый Структура(ФиксированныйКонтекст);
	Контекст.Вставить("Вложения", Новый Массив);
	Контекст.Вставить("Описание");
	Контекст.Вставить("Строка");
	
	Для Каждого ОписаниеСтроки Из Условие Цикл
		Строка = СтрокаТаблицы(ОписаниеСтроки, ТекущийКонтекст);
		Контекст.Строка = Строка;
		
		Если Строка.Вид = "Имя"
		 Или Строка.Вид = "Число"
		 Или Строка.Вид = "ПроизвольнаяСтрока"
		 Или Строка.Вид = "КлючевоеСлово" 
		   И (    Строка.Уточнение = "Истина"
		      Или Строка.Уточнение = "Ложь"
		      Или Строка.Уточнение = "Неопределено" ) Тогда
			
			НовоеОписание = ОписаниеУзлаПолеИлиУзлаКонстанта(Строка);
			ДобавитьАргументФункциюВыборОператор(Контекст, НовоеОписание);
			
		ИначеЕсли Строка.Вид = "КлючевоеСлово" Тогда
			
			Если Строка.Тип = "Функция"
			 Или Строка.Уточнение = "В" Тогда
				
				Если Строка.Тип = "Функция" Тогда
					РазобратьФункцию(Контекст);
				Иначе
					РазобратьСоединительВ(Контекст);
				КонецЕсли;
				
			ИначеЕсли Строка.Тип = "Соединитель" Тогда
				РазобратьСоединитель(Контекст);
			
			ИначеЕсли Строка.Тип = "Оператор" Тогда
				РазобратьОператор(Контекст);
				
			ИначеЕсли Строка.Тип = "СловоВыбора" Тогда
				РазобратьВыбор(Контекст);
			Иначе
				РазобратьОшибочноеКлючевоеСлово(Контекст);
			КонецЕсли;
			
		ИначеЕсли Строка.Вид = "Операция" Тогда
			РазобратьСоединитель(Контекст, Истина);
			
		ИначеЕсли Строка.Вид = "Разделитель" Тогда
			Если Строка.Символы = "(" Тогда
				НовоеОписание = Неопределено; // См. ОписаниеУзла
				РазобратьВыражение(Строка.Строки, НовоеОписание, Контекст);
				ДобавитьАргументФункциюВыборОператор(Контекст, НовоеОписание);
				Если НовоеОписание <> Неопределено Тогда
					НовоеОписание.Источник.Приоритет = 99;
				КонецЕсли;
			Иначе
				Контекст.Описание = Неопределено;
				УстановитьОшибкуВСтроке(Строка,
					НСтр("ru = 'Запятая может использоваться только для разделения параметров функций'"));
			КонецЕсли;
		Иначе
			Контекст.Описание = Неопределено;
			УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Обработка ключевого слова ""%1"" не определена'"), Строка.Символы));
		КонецЕсли;
		
		Если Контекст.Описание = Неопределено Тогда
			// Возникала ошибка, останавливающая дальнейший разбор (предотвращение ложных срабатываний).
			Прервать;
		КонецЕсли;
	КонецЦикла;
	
	Если Контекст.Описание = Неопределено Тогда
		Если ВложенноеВыражение Тогда
			Состав = Новый Структура("Источник, Узел, Значение", Строка, "Константа", Ложь);
		Иначе
			Состав = Неопределено;
		КонецЕсли;
		
	ИначеЕсли Контекст.Вложения.Количество() = 0 Тогда
		Состав = Контекст.Описание;
	Иначе
		Состав = Контекст.Вложения[Контекст.Вложения.Количество() - 1];
	КонецЕсли;
	
КонецПроцедуры

// Для процедур РазобратьВыражение, РазобратьПервыйПараметрПроверочнойФункции,
// РазобратьПараметрыФункцииТипЗначения, РазобратьВыбор и
// для функции ОписаниеУзлаПолеИзФункцииЕстьNull.
//
Функция ОписаниеУзлаПолеИлиУзлаКонстанта(Строка)
	
	// <Имя поля>, <Число>, <Произвольная строка>, Истина, Ложь, Неопределено.
	
	Если Строка.Вид = "Имя" Тогда
		НовоеОписание = ОписаниеУзлаПоле(Строка);
		НовоеОписание.Имя         = Строка.Символы;
		НовоеОписание.ИмяИсточник = Строка;
	Иначе
		СвойстваУзла = "Источник, Узел, Значение";
		НовоеОписание = Новый Структура(СвойстваУзла, Строка, "Константа");
		
		Если Строка.Вид = "КлючевоеСлово" Тогда
			Если Строка.Уточнение = "Истина" Тогда
				НовоеОписание.Значение = Истина;
				
			ИначеЕсли Строка.Уточнение = "Ложь" Тогда
				НовоеОписание.Значение = Ложь;
				
			Иначе // Строка.Вид = "Неопределено".
				НовоеОписание.Значение = Неопределено;
			КонецЕсли;
		Иначе // "Число" или "ПроизвольнаяСтрока".
			НовоеОписание.Значение = Строка.Уточнение;
		КонецЕсли;
	КонецЕсли;
	
	Возврат НовоеОписание;
	
КонецФункции

// Для функций ОписаниеУзлаПолеИлиУзлаКонстанта, ОписаниеУзлаПолеИзФункцииВыразить, ОписаниеУзлаПолеИзФункцииЕстьNull.
Функция ОписаниеУзлаПоле(Строка)
	
	СвойстваУзла = "Источник, Узел, Имя, Таблица, Псевдоним, Выразить, Вложение, ЕстьNull";
	НовоеОписание = Новый Структура(СвойстваУзла, Строка, "Поле");
	
	НовоеОписание.Вставить("ИмяИсточник",      Неопределено);
	НовоеОписание.Вставить("ВыразитьИсточник", Неопределено);
	НовоеОписание.Вставить("ЕстьNullИсточник", Неопределено);
	
	Возврат НовоеОписание;
	
КонецФункции

// Для процедуры РазобратьВыражение.
Процедура РазобратьСоединитель(Контекст, ЭтоОперация = Ложь)
	
	// И, Или, Как, Кроме, Только, Есть и любая операция =, <>, ...
	// Ключевое слово В разбирается отдельно в процедуре "РазобратьОперациюВ".
	
	Строка = Контекст.Строка; // СтрокаТаблицыЗначений из см. ТаблицаНаборовСимволов
	
	НовоеОписание = Новый Структура("Источник, Узел", Строка,
		?(Строка.Вид = "Операция", Строка.Символы, Строка.Уточнение));
	
	Если Строка.Уточнение = "И"
	 Или Строка.Уточнение = "Или" Тогда
		
		НовоеОписание.Вставить("Аргументы", Новый Массив);
		НовоеОписание.Аргументы.Добавить(Неопределено);
		ДобавитьСоединитель(Контекст, НовоеОписание, НовоеОписание.Аргументы[0]);
	
	ИначеЕсли Строка.Уточнение = "Есть" Тогда
		НовоеОписание.Узел = "ЕстьNull";
		НовоеОписание.Вставить("Аргумент", Неопределено);
		ДобавитьСоединитель(Контекст, НовоеОписание, НовоеОписание.Аргумент);
		// Проверка корректности параметров выполняется
		// в процедуре ОтметитьНекорректныеАргументыИЗапрещенныеУзлы.
		
	ИначеЕсли Строка.Вид = "Операция" Тогда
		НовоеОписание.Вставить("ПервыйАргумент", Неопределено);
		НовоеОписание.Вставить("ВторойАргумент", Неопределено);
		ДобавитьСоединитель(Контекст, НовоеОписание, НовоеОписание.ПервыйАргумент);
		// Проверка корректности аргументов выполняется
		// в процедуре ОтметитьНекорректныеАргументыИЗапрещенныеУзлы.
		
	ИначеЕсли Строка.Уточнение = "Как"
	      Или Строка.Уточнение = "Кроме"
	      Или Строка.Уточнение = "Только" Тогда
		
		Контекст.Описание = Неопределено;
		УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Ключевое слово ""%1"" может использоваться только в параметрах функций'"), Строка.Символы));
	Иначе
		Контекст.Описание = Неопределено;
		УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Обработка ключевого слова ""%1"" не определена'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьВыражение.
Процедура РазобратьСоединительВ(Контекст)
	
	Строка = Контекст.Строка;
	
	НовоеОписание = Новый Структура("Источник, Узел", Строка, Строка.Уточнение);
	НовоеОписание.Вставить("Искомое",  Неопределено);
	НовоеОписание.Вставить("Значения", Новый Массив);
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	// Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
	
	Для Каждого ОписаниеПараметра Из СоставПараметров Цикл
		СтрокиОписанияПараметра = ОписаниеПараметра.Строки; // Массив Из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
		Для Каждого Подстрока Из СтрокиОписанияПараметра Цикл
			
			РазобратьЗначениеСоединителяВ(Контекст, Подстрока, НовоеОписание);
			
			Если ОписаниеПараметра.Строки[0] <> Подстрока Тогда
				УстановитьОшибкуВСтроке(Подстрока, НСтр("ru = 'Перед параметром не указана запятая'"));
			КонецЕсли;
			
		КонецЦикла;
	КонецЦикла;
	
	ДобавитьСоединитель(Контекст, НовоеОписание, НовоеОписание.Искомое);
	
	Если НовоеОписание.Искомое.Узел <> "Поле" Тогда
		УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Операцию ""%1"" можно указывать только после имени поля'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьСоединительВ.
Процедура РазобратьЗначениеСоединителяВ(Контекст, Подстрока, НовоеОписание)
	
	Строка = Контекст.Строка;
	
	Если Подстрока.Вид = "Число"
	 Или Подстрока.Вид = "ПроизвольнаяСтрока" Тогда
		
		ОписаниеКонстанты = Новый Структура("Источник, Узел", Подстрока, "Константа");
		ОписаниеКонстанты.Вставить("Значение", Подстрока.Уточнение);
		НовоеОписание.Значения.Добавить(ОписаниеКонстанты);
		
	ИначеЕсли Подстрока.Вид = "КлючевоеСлово" Тогда
		
		Если Подстрока.Уточнение = "Значение" Тогда
			НовыйКонтекст = НовыйКонтекст(Контекст, Подстрока, Неопределено);
			РазобратьФункцию(НовыйКонтекст);
			Если НовыйКонтекст.Описание <> Неопределено Тогда
				НовоеОписание.Значения.Добавить(НовыйКонтекст.Описание);
			КонецЕсли;
		Иначе
			УстановитьОшибкуВСтроке(Подстрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'В списке значений операции ""%1"" ключевое слово ""%2"" недопустимо'"),
				Строка.Символы, Подстрока.Символы));
		КонецЕсли;
		
	ИначеЕсли Подстрока.Вид = "Имя" Тогда
		УстановитьОшибкуВСтроке(Подстрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В списке значений операции ""%1"" имя поля недопустимо'"), Строка.Символы));
		
	ИначеЕсли Подстрока.Символы = "(" Тогда
		УстановитьОшибкуВСтроке(Подстрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В списке значений операции ""%1"" скобки допустимы только для параметров функции'"), Строка.Символы));
	Иначе
		УстановитьОшибкуВСтроке(Подстрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В списке значений операции ""%1"" можно указывать только значения'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьЗначениеСоединителяВ.
Функция НовыйКонтекст(Контекст, Строка = null, Описание = null)
	
	ФиксированныйКонтекст = Новый ФиксированнаяСтруктура(Контекст);
	НовыйКонтекст = Новый Структура(ФиксированныйКонтекст);
	
	Если Строка <> null Тогда
		НовыйКонтекст.Строка = Строка;
	КонецЕсли;
	
	Если Описание <> null Тогда
		НовыйКонтекст.Описание = Описание;
	КонецЕсли;
	
	Возврат НовыйКонтекст;
	
КонецФункции

// Для процедур РазобратьСоединитель, РазобратьСоединительВ, ВставитьСоединительСУчетомПриоритета.
Процедура ДобавитьСоединитель(Контекст, НовоеОписание, ПервыйАргумент);
	
	// Добавляемый соединитель: И, Или, В, Есть и любая операция (=, <>, ...).
	
	Описание = Контекст.Описание; // См. ОписаниеУзла
	
	Если Описание = Неопределено Тогда
		Контекст.Описание = НовоеОписание;
		
	ИначеЕсли Описание.Узел = "И"
	      Или Описание.Узел = "Или" Тогда
		
		Если Описание.Аргументы.Количество() = 1 Тогда
			Описание.Аргументы.Добавить(Неопределено);
			ОбработатьПропущенныйАргументПослеСоединителя(Контекст, Описание.Аргументы[1]);
		КонецЕсли;
		ВставитьСоединительСУчетомПриоритета(Контекст,
			Описание.Аргументы[1], НовоеОписание, ПервыйАргумент);
		
	ИначеЕсли Описание.Узел = "Не" Тогда
		Если Не ЗначениеЗаполнено(Описание.Аргумент) Тогда
			ОбработатьПропущенныйАргументПослеСоединителя(Контекст, Описание.Аргумент);
		КонецЕсли;
		ВставитьСоединительСУчетомПриоритета(Контекст,
			Описание.Аргумент, НовоеОписание, ПервыйАргумент);
		
	ИначеЕсли Описание.Источник.Вид = "Операция" Тогда
		Если Не ЗначениеЗаполнено(Описание.ВторойАргумент) Тогда
			ОбработатьПропущенныйАргументПослеСоединителя(Контекст, Описание.ВторойАргумент, Ложь);
		КонецЕсли;
		ВставитьСоединительСУчетомПриоритета(Контекст,
			Описание.ВторойАргумент, НовоеОписание, ПервыйАргумент);
		
	ИначеЕсли СтрНайти(",Поле,Значение,Константа,В,ЕстьNull,Выбор,", "," + Описание.Узел + ",") > 0
	      Или Описание.Источник.Тип = "Функция" Тогда
		// Второй аргумент операции В уже разобран в процедуре РазобратьОперациюВ.
		// Второй аргумент Null операции Есть уже разобран в функции ФункцииСВыражениямиВСкобках.
		// Остальные узлы не имеют второго аргумента.
		ВставитьСоединительСУчетомПриоритета(Контекст, Неопределено, НовоеОписание, ПервыйАргумент);
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не определена обработка узла ""%1""'"), Описание.Узел);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ДобавитьСоединитель.
Процедура ОбработатьПропущенныйАргументПослеСоединителя(Контекст, ВторойАргумент, ЛогическаяОперация = Истина)
	
	Описание = Контекст.Описание; // См. ОписаниеУзла
	
	УстановитьОшибкуВСтроке(Описание.Источник,
		?(ЛогическаяОперация,
			НСтр("ru = 'Не указан аргумент после логической операции'"),
			НСтр("ru = 'Не указан аргумент после операции'")),
		Истина);
	
	ВторойАргумент = Новый Структура("Источник, Узел, Значение", Контекст.Строка, "Константа", Истина);
	
КонецПроцедуры

// Для процедур ДобавитьСоединитель, ОбработатьПропущеннуюЛогическуюОперацию.
Процедура ВставитьСоединительСУчетомПриоритета(Контекст,
			ПоследнийАргументОписания, НовоеОписание, ПервыйАргументНовогоОписания)
	
	Вложения = Контекст.Вложения;
	Описание = Контекст.Описание; // См. ОписаниеУзла
	
	Если ПоследнийАргументОписания <> Неопределено
	   И Контекст.Строка.Приоритет >= Описание.Источник.Приоритет Тогда
		
		// Замена аргумента текущего узла на соединитель (случай "А Или Б И ...").
		ПервыйАргументНовогоОписания = ПоследнийАргументОписания;
		ПоследнийАргументОписания = НовоеОписание;
		
		Вложения.Вставить(0, Контекст.Описание);
		Контекст.Описание = НовоеОписание;
		Возврат;
	КонецЕсли;
	
	Если Вложения.Количество() = 0 Тогда
		// Вложение текущего узла, как первого аргумента соединителя (случай "А И Б Или ...").
		ПервыйАргументНовогоОписания = Контекст.Описание;
		Контекст.Описание = НовоеОписание;
		Возврат;
	КонецЕсли;
	
	// Вложение предыдущего узла, как первого аргумента соединителя (случай "А И Не Б Или ...").
	Контекст.Описание = Вложения[0];
	Вложения.Удалить(0);
	
	ДобавитьСоединитель(Контекст, НовоеОписание, ПервыйАргументНовогоОписания);
	
КонецПроцедуры

// Для процедуры РазобратьВыражение.
Процедура РазобратьОператор(Контекст)
	
	// Оператор Не.
	
	НовоеОписание = Новый Структура("Источник, Узел, Аргумент",
		Контекст.Строка, Контекст.Строка.Уточнение);
	
	ДобавитьАргументФункциюВыборОператор(Контекст, НовоеОписание);
	
	Контекст.Вложения.Вставить(0, Контекст.Описание);
	Контекст.Описание = НовоеОписание;
	
КонецПроцедуры

// Для процедур РазобратьВыражение, РазобратьЗначениеСоединителяВ.
Процедура РазобратьФункцию(Контекст)
	
	Строка = Контекст.Строка;
	
	НовоеОписание = Новый Структура("Источник, Узел", Строка, Строка.Уточнение);
	
	Если Строка.Уточнение = "ЗначениеРазрешено"
	 Или Строка.Уточнение = "ЧтениеОбъектаРазрешено"
	 Или Строка.Уточнение = "ИзменениеОбъектаРазрешено"
	 Или Строка.Уточнение = "ЧтениеСпискаРазрешено"
	 Или Строка.Уточнение = "ИзменениеСпискаРазрешено"
	 Или Строка.Уточнение = "ЭтоАвторизованныйПользователь" Тогда
		
		РазобратьПараметрыПроверочнойФункции(Контекст, НовоеОписание);
		
	ИначеЕсли Строка.Уточнение = "ДляВсехСтрок"
	      Или Строка.Уточнение = "ДляОднойИзСтрок" Тогда
		
		ОписаниеВыражения = Неопределено;
		РазобратьВыражение(Строка.Строки, ОписаниеВыражения, Контекст);
		НовоеОписание.Вставить("Аргумент", ОписаниеВыражения);
	
	ИначеЕсли Строка.Уточнение = "Значение" Тогда
		РазобратьПараметрыФункцииЗначениеИлиФункцииТип(Строка, НовоеОписание, Истина, Контекст);
		
	ИначеЕсли Строка.Уточнение = "Тип" Тогда
		РазобратьПараметрыФункцииЗначениеИлиФункцииТип(Строка, НовоеОписание, Ложь, Контекст);
		
	ИначеЕсли Строка.Уточнение = "ТипЗначения" Тогда
		РазобратьПараметрыФункцииТипЗначения(Контекст, НовоеОписание);
		
	ИначеЕсли Строка.Уточнение = "ЕстьNull" Тогда
		НовоеОписание = ОписаниеУзлаПолеИзФункцииЕстьNull(Контекст.Строка, Контекст);
		
	ИначеЕсли Строка.Уточнение = "Выразить" Тогда
		НовоеОписание =  ОписаниеУзлаПолеИзФункцииВыразить(Контекст.Строка, Контекст);
		
	ИначеЕсли Строка.Уточнение = "ПравоДоступа" Тогда
		РазобратьПараметрыФункцииПравоДоступа(Строка, НовоеОписание, Контекст);
		
	ИначеЕсли Строка.Уточнение = "РольДоступна" Тогда
		РазобратьПараметрыФункцииРольДоступна(Строка, НовоеОписание, Контекст);
		
	ИначеЕсли Не Строка.ЭтоРезерв Тогда
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не определена обработка функции ""%1""'"), Строка.Уточнение);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	ДобавитьАргументФункциюВыборОператор(Контекст, НовоеОписание);
	
КонецПроцедуры

// Для процедуры РазобратьФункцию.
Процедура РазобратьПараметрыПроверочнойФункции(Контекст, НовоеОписание)
	
	Строка = Контекст.Строка;
	
	НовоеОписание.Вставить("Поле",                        Неопределено);
	НовоеОписание.Вставить("Типы",                        Новый Массив);
	НовоеОписание.Вставить("ПроверятьТипыКромеУказанных", Ложь);
	НовоеОписание.Вставить("УточненияСравнения",          Новый Соответствие);
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		Возврат; // Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
	КонецЕсли;
	
	РазобратьПервыйПараметрПроверочнойФункции(Контекст, СоставПараметров[0], НовоеОписание);
	
	Для Индекс = 1 По СоставПараметров.Количество() - 1 Цикл
		РазобратьДополнительныйПараметрПроверочнойФункции(Контекст,
			СоставПараметров[Индекс], НовоеОписание);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры РазобратьПараметрыПроверочнойФункции.
//
// Параметры:
//  ПервыйПараметр - Структура:
//   * Строки - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Процедура РазобратьПервыйПараметрПроверочнойФункции(Контекст, ПервыйПараметр, НовоеОписание)
	
	Если ПервыйПараметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.Поле = ОписаниеУзлаПолеИлиУзлаКонстанта(ПервыйПараметр.Строки[0]);
		
	ИначеЕсли ПервыйПараметр.Строки[0].Вид = "КлючевоеСлово"
	        И ПервыйПараметр.Строки[0].Уточнение = "Выразить" Тогда
		
		НовоеОписание.Поле = ОписаниеУзлаПолеИзФункцииВыразить(ПервыйПараметр.Строки[0], Контекст);
		
	ИначеЕсли ПервыйПараметр.Строки[0].Вид = "КлючевоеСлово"
	        И ПервыйПараметр.Строки[0].Уточнение = "ЕстьNull" Тогда
		
		НовоеОписание.Поле = ОписаниеУзлаПолеИзФункцииЕстьNull(ПервыйПараметр.Строки[0], Контекст);
	Иначе
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[0],
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Первым параметром может быть имя поля, функция ""%1"" или функция ""%2""'"),
				"Выразить,ЕстьNull"));
		Возврат;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() < 2 Тогда
		Возврат;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки[0].Вид = "КлючевоеСлово"
	   И ПервыйПараметр.Строки[0].Уточнение = "Выразить"
	   И НовоеОписание.Поле.Вложение = Неопределено Тогда
		
		УстановитьОшибкуВСтроке(СтрокаТаблицы(ПервыйПараметр.Строки[0].КонечнаяСтрока, Контекст),
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После вложенной функции ""%3"" должно быть указано имя поля через точку,
				           |если в параметре функции ""%4"" используется ключевое слово ""%1"" или ""%2""'"),
				"Только,Кроме",
				ПервыйПараметр.Строки[0].Символы,
				Контекст.Строка.Символы),
			Истина);
	КонецЕсли;
	
	Если ПервыйПараметр.Строки[1].Вид <> "КлючевоеСлово"
	 Или (  ПервыйПараметр.Строки[1].Уточнение <> "Только"
	      И ПервыйПараметр.Строки[1].Уточнение <> "Кроме" ) Тогда
		
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[1],
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После описания поля может быть указано, либо ключевое слово ""%1"", либо ""%2""'"),
				"Только,Кроме"));
		Возврат;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки[1].Уточнение = "Кроме" Тогда
		НовоеОписание.ПроверятьТипыКромеУказанных = Истина;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() < 3 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[1],  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'После ключевого слова ""%1"" не указан тип (имя таблицы)'"), ПервыйПараметр.Строки[1].Символы));
		Возврат;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки[2].Вид <> "Имя"
	   И ПервыйПараметр.Строки[2].Символы <> "(" Тогда
	
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[2],  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'После ключевого слова ""%1"" должен быть указан, либо тип (имя таблицы), либо список типов в скобках'"),
			ПервыйПараметр.Строки[1].Символы));
		Возврат;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() > 3 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[3], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Перед параметром функции ""%1"" не указана запятая'"), Контекст.Строка.Символы));
	КонецЕсли;
	
	Если ПервыйПараметр.Строки[2].Вид = "Имя" Тогда
		НовоеОписание.Типы.Добавить(ПервыйПараметр.Строки[2]);
		ДобавитьТребуемуюТаблицуКакСсылочныйТип(Контекст, ПервыйПараметр.Строки[2].Символы, ПервыйПараметр.Строки[2]);
		Возврат;
	КонецЕсли;
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(ПервыйПараметр.Строки[2], Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		Возврат; // Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
	КонецЕсли;
	
	Для Каждого Параметр Из СоставПараметров Цикл
		
		Если Параметр.Строки[0].Вид = "Имя"
		   И Параметр.Строки.Количество() < 2 Тогда
			
			НовоеОписание.Типы.Добавить(Параметр.Строки[0]);
		Иначе
			УстановитьОшибкуВСтроке(Параметр.Строки[?(Параметр.Строки.Количество() < 2, 0, 1)],
				НСтр("ru = 'В списке типов могут быть указаны только имена таблицы через запятую'"));
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры РазобратьПараметрыПроверочнойФункции.
// 
// Параметры:
//  Параметр - Структура:
//    * Строки - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Процедура РазобратьДополнительныйПараметрПроверочнойФункции(Контекст, Параметр, НовоеОписание)
	
	Если Параметр.Строки[0].Вид = "Имя"
	 Или Параметр.Строки[0].Вид = "КлючевоеСлово"
	   И (    Параметр.Строки[0].Тип = "ЗначениеСравнения"
	      Или Параметр.Строки[0].Тип = "ИмяТипа" ) Тогда
		
		Если Параметр.Строки[0].Вид = "Имя" Тогда
			ЗначениеСравнения = Параметр.Строки[0];
			ДобавитьТребуемуюТаблицуКакСсылочныйТип(Контекст, Параметр.Строки[0].Символы, Параметр.Строки[0]);
		Иначе
			ЗначениеСравнения = Параметр.Строки[0].Уточнение;
		КонецЕсли;
	Иначе
		УстановитьОшибкуВСтроке(Параметр.Строки[0],
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Дополнительным параметром может быть тип (имя таблицы),
				           |""%1"", ""%2"", ""%3"", ""%4"", ""%5"", ""%6"" и ""%7""'"),
				"ПустаяСсылка,Неопределено,Null,Число,Строка,Дата,Булево"));
		Возврат;
	КонецЕсли;
	
	Если Параметр.Строки.Количество() < 2 Тогда
		Если Параметр.Строки[0].Вид = "Имя" Тогда 
			Шаблон = НСтр("ru = 'После типа (имени таблицы) ""%2"" должно быть указано ключевое слово ""%1""'");
		Иначе
			Шаблон = НСтр("ru = 'После ключевого слова ""%2"" должно быть указано ключевое слово ""%1""'");
		КонецЕсли;
		УстановитьОшибкуВСтроке(Параметр.Строки[0], ПодставитьКлючевыеСловаВСтроку(Контекст,
				Шаблон, "Как", Параметр.Строки[0].Символы),
			Истина);
		Возврат;
	КонецЕсли;
	
	Если Параметр.Строки[1].Вид <> "КлючевоеСлово"
	 Или Параметр.Строки[1].Уточнение <> "Как" Тогда
		
		Если Параметр.Строки[0].Вид = "Имя" Тогда
			Шаблон = НСтр("ru = 'После типа (имени таблицы) ""%2"" должно быть указано ключевое слово ""%1""'");
		Иначе
			Шаблон = НСтр("ru = 'После ключевого слова ""%2"" должно быть указано ключевое слово ""%1""'");
		КонецЕсли;
		УстановитьОшибкуВСтроке(Параметр.Строки[1], ПодставитьКлючевыеСловаВСтроку(Контекст,
			Шаблон, "Как", Параметр.Строки[0].Символы));
		Возврат;
	КонецЕсли;
	
	Если Параметр.Строки.Количество() < 3 Тогда
		УстановитьОшибкуВСтроке(Параметр.Строки[1], ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После ключевого слова ""%4"" не указано значение уточнения ""%1"", ""%2"" или ""%3""'"),
				"Ложь,Истина,Пусто",
				Параметр.Строки[1].Символы),
			Истина);
		Возврат;
	КонецЕсли;
	
	Если Параметр.Строки[2].Вид <> "КлючевоеСлово"
	 Или Параметр.Строки[2].Тип <> "ЗначениеУточнения" Тогда
		
		УстановитьОшибкуВСтроке(Параметр.Строки[2], ПодставитьКлючевыеСловаВСтроку(Контекст,
			НСтр("ru = 'После ключевого слова ""%4"" должно быть указано значение уточнения ""%1"", ""%2"" или ""%3""'"),
			"Ложь,Истина,Пусто",
			Параметр.Строки[1].Символы));
	Иначе
		НовоеОписание.УточненияСравнения.Вставить(ЗначениеСравнения, Параметр.Строки[2].Уточнение);
	КонецЕсли;
	
	Если Параметр.Строки.Количество() > 3 Тогда
		УстановитьОшибкуВСтроке(Параметр.Строки[3],
			НСтр("ru = 'Перед параметром не указана запятая или лишний параметр'"));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьФункцию.
Процедура РазобратьПараметрыФункцииЗначениеИлиФункцииТип(Строка, НовоеОписание, ЭтоФункцияЗначение, Контекст)
	
	НовоеОписание.Вставить("Имя", Неопределено);
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		Возврат; // Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
	КонецЕсли;
	
	Параметр = СоставПараметров[0];
	Если Параметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.Имя = Параметр.Строки[0].Символы;
		
		Если ЭтоФункцияЗначение Тогда
			ДобавитьТребуемыйПредопределенныйЭлемент(Контекст, НовоеОписание.Имя, Параметр.Строки[0]);
		Иначе
			ДобавитьТребуемуюТаблицуКакСсылочныйТип(Контекст, НовоеОписание.Имя, Параметр.Строки[0]);
		КонецЕсли;
		
	ИначеЕсли ЭтоФункцияЗначение Тогда
		УстановитьОшибкуВСтроке(Параметр.Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В функции ""%1"" можно указать только имя предопределенного значения'"), Строка.Символы));
	
	ИначеЕсли Параметр.Строки[0].Вид = "КлючевоеСлово"
	        И Параметр.Строки[0].Тип = "ИмяТипа" Тогда
		
		НовоеОписание.Имя = Параметр.Строки[0].Уточнение;
	Иначе
		УстановитьОшибкуВСтроке(Параметр.Строки[0], ПодставитьКлючевыеСловаВСтроку(Контекст,
			НСтр("ru = 'В функции ""%5"" можно указать имя таблицы или ""%1"", ""%2"", ""%3"" и ""%4""'"),
			"Число,Строка,Дата,Булево",
			Строка.Символы));
	КонецЕсли;
	
	Если Параметр.Строки.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(Параметр.Строки[1], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
	Если СоставПараметров.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(СоставПараметров[1].Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьФункцию.
Процедура РазобратьПараметрыФункцииРольДоступна(Строка, НовоеОписание, Контекст)
	
	НовоеОписание.Вставить("ИмяРоли", Неопределено);
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		Возврат; // Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
	КонецЕсли;
	
	Параметр = СоставПараметров[0];
	Если Параметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.ИмяРоли = Параметр.Строки[0].Символы;
		
		ПроверитьИмяРоли(НовоеОписание.ИмяРоли, Параметр.Строки[0]);
	Иначе
		УстановитьОшибкуВСтроке(Параметр.Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В функции ""%1"" можно указать только имя роли'"), Строка.Символы));
	КонецЕсли;
	
	Если Параметр.Строки.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(Параметр.Строки[1], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
	Если СоставПараметров.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(СоставПараметров[1].Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьФункцию.
Процедура РазобратьПараметрыФункцииПравоДоступа(Строка, НовоеОписание, Контекст)
	
	НовоеОписание.Вставить("ИмяПрава", Неопределено);
	НовоеОписание.Вставить("ПолноеИмяОбъектаМетаданных", Неопределено);
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		Возврат; // Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
	КонецЕсли;
	
	ПервыйПараметр = СоставПараметров[0];
	Если ПервыйПараметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.ИмяПрава = ПервыйПараметр.Строки[0].Символы;
	Иначе
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В функции ""%1"" в первом параметре можно указать только имя права'"), Строка.Символы));
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[1], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Перед параметром ""%1"" функции ""%2"" не указана запятая'"),
			ПервыйПараметр.Строки[1].Символы,
			Строка.Символы));
	КонецЕсли;
	
	Если СоставПараметров.Количество() < 2 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.КонечнаяСтрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" должно быть два параметра'"), Строка.Символы));
		Возврат;
	КонецЕсли;
	
	ВторойПараметр = СоставПараметров[1];
	Если ВторойПараметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.ПолноеИмяОбъектаМетаданных = ВторойПараметр.Строки[0].Символы;
		
		ПроверитьИмяПраваОбъектаМетаданных(НовоеОписание.ИмяПрава, ПервыйПараметр.Строки[0],
			НовоеОписание.ПолноеИмяОбъектаМетаданных, ВторойПараметр.Строки[0]);
	Иначе
		УстановитьОшибкуВСтроке(ВторойПараметр.Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В функции ""%1"" во втором параметре можно указать только полное имя объекта метаданных'"),
			Строка.Символы));
	КонецЕсли;
	
	Если ВторойПараметр.Строки.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(ВторойПараметр.Строки[1], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только два параметра'"), Строка.Символы));
	КонецЕсли;
	
	Если СоставПараметров.Количество() > 2 Тогда
		УстановитьОшибкуВСтроке(СоставПараметров[2].Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только два параметра'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьФункцию.
Процедура РазобратьПараметрыФункцииТипЗначения(Контекст, НовоеОписание)
	
	Строка = Контекст.Строка;
	НовоеОписание.Вставить("Аргумент", Неопределено);
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		Возврат; // Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
	КонецЕсли;
	
	Параметр = СоставПараметров[0];
	
	Если Параметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.Аргумент = ОписаниеУзлаПолеИлиУзлаКонстанта(Параметр.Строки[0]);
		
	ИначеЕсли Параметр.Строки[0].Вид = "КлючевоеСлово"
	        И Параметр.Строки[0].Уточнение = "Выразить" Тогда
		
		НовоеОписание.Аргумент = ОписаниеУзлаПолеИзФункцииВыразить(Параметр.Строки[0], Контекст);
		
	ИначеЕсли Параметр.Строки[0].Вид = "КлючевоеСлово"
	        И Параметр.Строки[0].Уточнение = "ЕстьNull" Тогда
		
		НовоеОписание.Аргумент = ОписаниеУзлаПолеИзФункцииЕстьNull(Параметр.Строки[0], Контекст);
	Иначе
		УстановитьОшибкуВСтроке(Параметр.Строки[0],
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Параметром может быть имя поля, функция ""%1"" или функция ""%2""'"),
				"Выразить,ЕстьNull"));
		Возврат;
	КонецЕсли;
	
	Если Параметр.Строки[0].Вид = "КлючевоеСлово"
	   И Параметр.Строки[0].Уточнение = "Выразить"
	   И НовоеОписание.Аргумент.Вложение = Неопределено Тогда
		
		УстановитьОшибкуВСтроке(СтрокаТаблицы(Параметр.Строки[0].КонечнаяСтрока, Контекст),
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После вложенной функции ""%1"" должно быть указано имя поля через точку'"),
				Параметр.Строки[0].Символы),
			Истина);
	КонецЕсли;
	
	Если Параметр.Строки.Количество() < 2 Тогда
		Возврат;
	КонецЕсли;
	
	Если Параметр.Строки.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(Параметр.Строки[1], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
	Если СоставПараметров.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(СоставПараметров[1].Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедур РазобратьФункцию, РазобратьПервыйПараметрПроверочнойФункции, РазобратьПараметрыФункцииТипЗначения.
Функция ОписаниеУзлаПолеИзФункцииВыразить(Строка, Контекст)
	
	НовоеОписание = ОписаниеУзлаПоле(Строка);
	
	Если Строка.Строки.Количество() > 0 Тогда
		ПоследняяСтрока = СтрокаТаблицы(Строка.Строки[Строка.Строки.Количество() - 1], Контекст);
		Если ПоследняяСтрока.Тип = "ДополнениеКВыразить" Тогда
			Строка.Строки.Удалить(Строка.Строки.Количество() - 1);
			НовоеОписание.Имя         = Сред(ПоследняяСтрока.Символы, 2);
			НовоеОписание.ИмяИсточник = ПоследняяСтрока;
			НовоеОписание.Вложение    = ОписаниеУзлаПолеИзФункцииВыразить(Строка, Контекст);
			Возврат НовоеОписание;
		КонецЕсли;
	КонецЕсли;
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		// Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
		Возврат НовоеОписание;
	КонецЕсли;
	
	ПервыйПараметр = СоставПараметров[0];
	
	Если ПервыйПараметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.Имя         = ПервыйПараметр.Строки[0].Символы;
		НовоеОписание.ИмяИсточник = ПервыйПараметр.Строки[0];
		
	ИначеЕсли ПервыйПараметр.Строки[0].Вид = "КлючевоеСлово"
	        И ПервыйПараметр.Строки[0].Уточнение = "Выразить" Тогда
		
		НовоеОписание.Вложение = ОписаниеУзлаПолеИзФункцииВыразить(ПервыйПараметр.Строки[0], Контекст);
		Если НовоеОписание.Вложение.Вложение = Неопределено Тогда
			УстановитьОшибкуВСтроке(СтрокаТаблицы(ПервыйПараметр.Строки[0].КонечнаяСтрока, Контекст),
				СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'После вложенной функции ""%1"" должно быть указано имя поля через точку'"),
					ПервыйПараметр.Строки[0].Символы),
				Истина);
		Иначе
			НовоеОписание = НовоеОписание.Вложение;
		КонецЕсли;
		
	ИначеЕсли ПервыйПараметр.Строки[0].Вид = "КлючевоеСлово"
	        И ПервыйПараметр.Строки[0].Уточнение = "ЕстьNull" Тогда
		
		НовоеОписание = ОписаниеУзлаПолеИзФункцииЕстьNull(ПервыйПараметр.Строки[0], Контекст);
	Иначе
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[0],
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'Первым параметром может быть имя поля, функция ""%1"" или функция ""%2""'"),
				"Выразить,ЕстьNull"));
		Возврат НовоеОписание;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() < 2 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[0],
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После описания поля должно быть указано ключевое слово ""%1""'"), "Как"), Истина);
		Возврат НовоеОписание;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки[1].Вид <> "КлючевоеСлово"
	 Или ПервыйПараметр.Строки[1].Уточнение <> "Как" Тогда
		
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[1],
			ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После описания поля должно быть указано ключевое слово ""%1""'"), "Как"));
		Возврат НовоеОписание;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() < 3 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[1],  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'После ключевого слова ""%1"" не указан тип (имя таблицы)'"), ПервыйПараметр.Строки[1].Символы));
		Возврат НовоеОписание;
	КонецЕсли;
	
	Если ПервыйПараметр.Строки[2].Вид <> "Имя" Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[2],  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'После ключевого слова ""%1"" должен быть указан тип (имя таблицы)'"),
			ПервыйПараметр.Строки[1].Символы));
	Иначе
		НовоеОписание.Выразить = ПервыйПараметр.Строки[2].Символы;
		НовоеОписание.ВыразитьИсточник = ПервыйПараметр.Строки[2];
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() > 3 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[3], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
	Если СоставПараметров.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.КонечнаяСтрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только один параметр'"), Строка.Символы));
	КонецЕсли;
	
	Возврат НовоеОписание;
	
КонецФункции

// Для процедур РазобратьФункцию, РазобратьПервыйПараметрПроверочнойФункции,
// РазобратьПараметрыФункцииТипЗначения и
// для функции ОписаниеУзлаПолеИзФункцииВыразить.
//
Функция ОписаниеУзлаПолеИзФункцииЕстьNull(Строка, Контекст)
	
	НовоеОписание = ОписаниеУзлаПоле(Строка);
	НовоеОписание.ЕстьNullИсточник = Строка;
	
	СоставПараметров = ПараметрыРазделенныеЗапятыми(Строка, Контекст);
	
	Если СоставПараметров.Количество() = 0 Тогда
		// Ошибка отсутствия параметров уже установлена в функции ФункцииСВыражениямиВСкобках.
		Возврат НовоеОписание;
	КонецЕсли;
	
	ПервыйПараметр = СоставПараметров[0];
	
	Если ПервыйПараметр.Строки[0].Вид = "Имя" Тогда
		НовоеОписание.Имя         = ПервыйПараметр.Строки[0].Символы;
		НовоеОписание.ИмяИсточник = ПервыйПараметр.Строки[0];
	Иначе
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Первым параметром функции ""%1"" может быть только имя поля'"), Строка.Символы));
	КонецЕсли;
	
	Если ПервыйПараметр.Строки.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.Строки[1], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Перед параметром функции ""%1"" не указана запятая'"), Строка.Символы));
		Возврат НовоеОписание;
	КонецЕсли;
	
	Если СоставПараметров.Количество() < 2 Тогда
		УстановитьОшибкуВСтроке(ПервыйПараметр.КонечнаяСтрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" должно быть два параметра'"), Строка.Символы));
		Возврат НовоеОписание;
	КонецЕсли;
	
	ВторойПараметр = СоставПараметров[1];
	
	Если ВторойПараметр.Строки[0].Вид = "Число"
	 Или ВторойПараметр.Строки[0].Вид = "ПроизвольнаяСтрока"
	 Или ВторойПараметр.Строки[0].Вид = "КлючевоеСлово"
	   И (    ВторойПараметр.Строки[0].Уточнение = "Истина"
	      Или ВторойПараметр.Строки[0].Уточнение = "Ложь"
	      Или ВторойПараметр.Строки[0].Уточнение = "Неопределено"
	      Или ВторойПараметр.Строки[0].Уточнение = "Значение" ) Тогда
		
		Если ВторойПараметр.Строки[0].Уточнение = "Значение" Тогда
			НовоеОписание.ЕстьNull = Новый Структура("Источник, Узел",
				ВторойПараметр.Строки[0], ВторойПараметр.Строки[0].Уточнение);
			
			РазобратьПараметрыФункцииЗначениеИлиФункцииТип(ВторойПараметр.Строки[0], НовоеОписание.ЕстьNull, Истина, Контекст);
		Иначе
			НовоеОписание.ЕстьNull = ОписаниеУзлаПолеИлиУзлаКонстанта(ВторойПараметр.Строки[0]);
		КонецЕсли;
	Иначе
		УстановитьОшибкуВСтроке(ВторойПараметр.Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" второй параметр может быть, либо предопределенным значением, либо константой'"),
			Строка.Символы));
	КонецЕсли;
	
	Если ВторойПараметр.Строки.Количество() > 1 Тогда
		УстановитьОшибкуВСтроке(ВторойПараметр.Строки[1], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" может быть только два параметра'"), Строка.Символы));
	КонецЕсли;
	
	Если СоставПараметров.Количество() > 2 Тогда
		УстановитьОшибкуВСтроке(СоставПараметров[2].Строки[0], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'У функции ""%1"" должно быть только два параметра'"), Строка.Символы));
	КонецЕсли;
	
	Возврат НовоеОписание;
	
КонецФункции

// Для процедур РазобратьСоединительВ, РазобратьПараметрыПроверочнойФункции,
// РазобратьПараметрыФункцииЗначениеИлиФункцииТип, РазобратьПараметрыФункцииТипЗначения и
// для функций ОписаниеУзлаПолеИзФункцииВыразить, ОписаниеУзлаПолеИзФункцииЕстьNull.
//
// Параметры:
//  ОписаниеСтроки - см. СтрокаТаблицы.ОписаниеСтроки
//
// Возвращаемое значение:
//   Массив из Структура:
//   * Строки - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Функция ПараметрыРазделенныеЗапятыми(ОписаниеСтроки, Контекст)
	
	СоставПараметров = Новый Массив;
	Строка = СтрокаТаблицы(ОписаниеСтроки, Контекст);
	
	Если Строка.Строки.Количество() = 0 Тогда
		Возврат СоставПараметров;
	КонецЕсли;
	
	ОписаниеПараметра = Новый Структура("Строки, КонечнаяСтрока", Новый Массив);
	ПредыдущаяПодстрокаЭтоЧастьАргумента = Ложь;
	
	Для Каждого ОписаниеПодстроки Из Строка.Строки Цикл
		Подстрока = СтрокаТаблицы(ОписаниеПодстроки, Контекст);
		Если Подстрока.Символы = "," Тогда
			Если Не ПредыдущаяПодстрокаЭтоЧастьАргумента Тогда
				Если Строка.Строки[0] = ОписаниеПодстроки Тогда
					УстановитьОшибкуВСтроке(Подстрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Перед запятой не указан параметр'"), Подстрока.Символы));
					ОписаниеПараметра.Строки.Добавить(ДополнительнаяСтрока(Подстрока, ""));
				Иначе
					УстановитьОшибкуВСтроке(Подстрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Пропущен параметр или лишняя запятая'"), Подстрока.Символы));
				КонецЕсли;
			КонецЕсли;
			Если ОписаниеПараметра.Строки.Количество() > 0 Тогда
				СоставПараметров.Добавить(ОписаниеПараметра);
				ОписаниеПараметра.КонечнаяСтрока = Подстрока;
			КонецЕсли;
			ОписаниеПараметра = Новый Структура("Строки, КонечнаяСтрока", Новый Массив);
			ПредыдущаяПодстрокаЭтоЧастьАргумента = Ложь;
			Продолжить;
		КонецЕсли;
		
		ПредыдущаяПодстрокаЭтоЧастьАргумента = Истина;
		
		ОписаниеПараметра.Строки.Добавить(Подстрока);
	КонецЦикла;
	
	Если ОписаниеПараметра.Строки.Количество() > 0 Тогда
		СоставПараметров.Добавить(ОписаниеПараметра);
		ОписаниеПараметра.КонечнаяСтрока = СтрокаТаблицы(Строка.КонечнаяСтрока, Контекст);
	Иначе
		УстановитьОшибкуВСтроке(Подстрока, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Лишняя запятая или после запятой не указан параметр'"), Подстрока.Символы), Истина);
	КонецЕсли;
	
	Возврат СоставПараметров;
	
КонецФункции

// Для процедуры РазобратьВыражение.
// 
// Параметры:
//    Контекст - Структура:
//    * Строка - Структура:
//       ** Строки - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Процедура РазобратьВыбор(Контекст)
	
	Строка = Контекст.Строка;
	
	СвойстваУзла = "Источник, Узел, Выбор, Когда, Иначе";
	НовоеОписание = Новый Структура(СвойстваУзла, Строка, "Выбор");
	НовоеОписание.Когда = Новый Массив;
	
	ПропуститьАнализКогда = Ложь;
	
	Выбор = СтрокаТаблицы(Строка.Строки[0], Контекст);
	Если Выбор.Строки.Количество() > 0 Тогда
		ПерваяСтрокаВыбора = СтрокаТаблицы(Выбор.Строки[0], Контекст);
		Если ПерваяСтрокаВыбора.Вид = "Имя" Тогда
			НовоеОписание.Выбор = ОписаниеУзлаПолеИлиУзлаКонстанта(ПерваяСтрокаВыбора);
		Иначе
			ПропуститьАнализКогда = Истина;
			УстановитьОшибкуВСтроке(ПерваяСтрокаВыбора, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Когда аргумент ключевого слова ""%1"" указан, то это может быть только имя поля'"),
				Строка.Символы));
		КонецЕсли;
	КонецЕсли;
	
	Индекс = 1;
	Пока Истина Цикл
		СледующаяСтрока = СтрокаТаблицы(Строка.Строки[Индекс], Контекст);
		Если СледующаяСтрока.Уточнение <> "Когда" Тогда
			Прервать;
		КонецЕсли;
		ОписаниеКогдаТогда = Новый Структура("Условие, Значение");
		НовоеОписаниеКогда = НовоеОписание.Когда; // Массив
		НовоеОписаниеКогда.Добавить(ОписаниеКогдаТогда);
		
		Когда = СледующаяСтрока;
		
		Если Не ПропуститьАнализКогда Тогда
			ПерваяПодстрокаКогда = ?(Когда.Строки.Количество() = 0, Неопределено,
				СтрокаТаблицы(Когда.Строки[0], Контекст));
			
			Если НовоеОписание.Выбор = Неопределено Тогда
				РазобратьВыражение(Когда.Строки, ОписаниеКогдаТогда.Условие, Контекст);
				
			ИначеЕсли Когда.Строки.Количество() = 0 Тогда
				Если ЗначениеЗаполнено(Когда.Символы) Тогда
					УстановитьОшибкуВСтроке(Когда, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Так как после ключевого слова ""%1"" указано поле, то после ключевого слова ""%2""
						           |должно быть указано, либо предопределенное значение, либо константа'"),
						Строка.Символы,
						Когда.Символы));
				КонецЕсли;
				
			ИначеЕсли ПерваяПодстрокаКогда.Вид = "Число"
			      Или ПерваяПодстрокаКогда.Вид = "ПроизвольнаяСтрока"
			      Или ПерваяПодстрокаКогда.Вид = "КлючевоеСлово"
			        И (    ПерваяПодстрокаКогда.Уточнение = "Истина"
			           Или ПерваяПодстрокаКогда.Уточнение = "Ложь"
			           Или ПерваяПодстрокаКогда.Уточнение = "Неопределено"
			           Или ПерваяПодстрокаКогда.Уточнение = "Значение" ) Тогда
				
				Если ПерваяПодстрокаКогда.Уточнение = "Значение" Тогда
					ОписаниеКогдаТогда.Условие = Новый Структура("Источник, Узел",
						ПерваяПодстрокаКогда, ПерваяПодстрокаКогда.Уточнение);
					
					РазобратьПараметрыФункцииЗначениеИлиФункцииТип(ПерваяПодстрокаКогда,
						ОписаниеКогдаТогда.Условие, Истина, Контекст);
				Иначе
					ОписаниеКогдаТогда.Условие = ОписаниеУзлаПолеИлиУзлаКонстанта(ПерваяПодстрокаКогда);
				КонецЕсли;
			ИначеЕсли ЗначениеЗаполнено(Когда.Символы) Тогда
				УстановитьОшибкуВСтроке(ПерваяПодстрокаКогда, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Так как после ключевого слова ""%1"" указано поле, то после ключевого слова ""%2""
					           |должно быть указано, либо предопределенное значение, либо константа'"),
					Строка.Символы,
					Когда.Символы));
			КонецЕсли;
		КонецЕсли;
		
		СоставТогда = СтрокаТаблицы(Строка.Строки[Индекс + 1], Контекст);
		Если СоставТогда.Строки.Количество() > 0 Тогда
			РазобратьВыражение(СоставТогда.Строки, ОписаниеКогдаТогда.Значение, Контекст);
			
		ИначеЕсли ЗначениеЗаполнено(СоставТогда.Символы) Тогда
			УстановитьОшибкуВСтроке(ПерваяПодстрокаКогда, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'После ключевого слова ""%1"" должно быть указано логическое выражение'"), СоставТогда.Символы));
		КонецЕсли;
		
		Индекс = Индекс + 2;
	КонецЦикла;
	
	РазобратьВыражение(СледующаяСтрока.Строки, НовоеОписание.Иначе, Контекст);
	
	ДобавитьАргументФункциюВыборОператор(Контекст, НовоеОписание);
	
КонецПроцедуры

// Для процедуры РазобратьВыражение.
Процедура РазобратьОшибочноеКлючевоеСлово(Контекст)
	
	Строка = Контекст.Строка;
	Контекст.Описание = Неопределено;
	
	Если Строка.Тип = "Неопределен" Тогда
		// Для зарезервированных слов ошибка уже установлена.
		Возврат;
	КонецЕсли;
	
	Если Строка.Тип = "ЗначениеСравнения" Тогда
		Если Строка.Уточнение = "Отключено" Тогда
			// "Отключено".
			УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Значение ""%1"" может использоваться только как уточняемое значение
				           |в параметрах функции ""%2""'"),
				Строка.Символы,
				КлючевоеСловоСУчетомЯзыка("ЗначениеРазрешено", Контекст)));
		Иначе
			// "ПустаяСсылка" или "Null".
			УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Значение ""%1"" может использоваться только как уточняемое значение
				           |в параметрах функций проверки разрешений'"),
				Строка.Символы));
		КонецЕсли;
		
	ИначеЕсли Строка.Тип = "ИмяТипа" Тогда
		// "Число", "Строка", "Дата", "Булево".
		УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
			НСтр("ru = 'Имя типа ""%1"" может использоваться только, как параметр функции ""%2"" или
			           |как уточняемое значение в параметрах функций проверки разрешений'"),
			Строка.Символы,
			"Тип"));
	Иначе
		УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Обработка ключевого слова ""%1"" не определена'"), Строка.Символы));
	КонецЕсли;
	
КонецПроцедуры

// Для процедур РазобратьВыражение, РазобратьОператор, РазобратьФункцию, РазобратьВыбор.
Процедура ДобавитьАргументФункциюВыборОператор(Контекст, ДобавляемоеОписание)
	
	// Текущий узел: Любой.
	// Добавляемое описание: Поле, Значение, Константа, Не, Выбор, любая функция.
	
	Описание = Контекст.Описание; // См. ОписаниеУзла
	
	Если Описание = Неопределено Тогда
		Контекст.Описание = ДобавляемоеОписание;
		
	ИначеЕсли Описание.Узел = "И"
	      Или Описание.Узел = "Или" Тогда
		
		Если Описание.Аргументы.Количество() = 1 Тогда
			Описание.Аргументы.Добавить(ДобавляемоеОписание);
		Иначе
			ОбработатьПропущеннуюЛогическуюОперацию(Контекст, Описание.Аргументы[1], ДобавляемоеОписание);
		КонецЕсли;
		
	ИначеЕсли Описание.Узел = "Не" Тогда
		
		Если Не ЗначениеЗаполнено(Описание.Аргумент) Тогда
			Описание.Аргумент = ДобавляемоеОписание;
		Иначе
			ОбработатьПропущеннуюЛогическуюОперацию(Контекст, Описание.Аргумент, ДобавляемоеОписание);
		КонецЕсли;
		
	ИначеЕсли Описание.Источник.Вид = "Операция" Тогда
		
		Если Не ЗначениеЗаполнено(Описание.ВторойАргумент) Тогда
			Описание.ВторойАргумент = ДобавляемоеОписание;
			// Проверка корректности аргументов выполняется
			// в процедуре ОтметитьНекорректныеАргументыИЗапрещенныеУзлы.
		Иначе
			ОбработатьПропущеннуюЛогическуюОперацию(Контекст, Описание.ВторойАргумент, ДобавляемоеОписание);
		КонецЕсли;
		
	ИначеЕсли СтрНайти(",Поле,Значение,Константа,В,ЕстьNull,Выбор,", "," + Описание.Узел + ",") > 0
	      Или Описание.Источник.Тип = "Функция" Тогда
		// Второй аргумент операции В уже разобран в процедуре РазобратьОперациюВ.
		// Второй аргумент Null операции Есть уже разобран в функции ФункцииСВыражениямиВСкобках.
		// Остальные узлы не имеют второго аргумента.
		ОбработатьПропущеннуюЛогическуюОперацию(Контекст, Неопределено, ДобавляемоеОписание);
	Иначе
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не определена обработка узла ""%1""'"), Описание.Узел);
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ДобавитьАргументФункциюВыборОператор
Процедура ОбработатьПропущеннуюЛогическуюОперацию(Контекст, ПоследнийАргументОписания, ДобавляемоеОписание)
	
	УстановитьОшибкуВСтроке(Контекст.Строка, НСтр("ru = 'Не указана логическая операция'"));
	
	// Восстановление.
	ДополнительнаяСтрока = ДополнительнаяСтрока(Контекст.Строка, "И", Контекст);
	
	НовоеОписание = Новый Структура("Источник, Узел, Аргументы", ДополнительнаяСтрока, "И", Новый Массив);
	НовоеОписание.Аргументы.Добавить();
	
	ТекущаяСтрока = Контекст.Строка;
	Контекст.Строка = ДополнительнаяСтрока;
	
	ВставитьСоединительСУчетомПриоритета(Контекст,
		ПоследнийАргументОписания, НовоеОписание, НовоеОписание.Аргументы[0]);
	
	Контекст.Строка = ТекущаяСтрока;
	
	НовоеОписание.Аргументы.Добавить(ДобавляемоеОписание);
	
КонецПроцедуры

// Для процедуры РазобратьУсловие.
//
// Возвращаемое значение:
//  Массив из СтрокаТаблицыЗначений
//
Функция ВыраженияВСкобкахВоВложениях(Строки, Контекст)
	
	Результат = Новый Массив;
	
	ТекущееВложение = Новый Структура("Строки", Результат);
	Вложения = Новый Массив;
	Вложения.Добавить(ТекущееВложение);
	
	Для Каждого Строка Из Строки Цикл
		Если Строка.Символы = "(" Тогда
			ДобавитьВложение(Строка, Вложения, ТекущееВложение, Контекст);
			
		ИначеЕсли Строка.Символы = ")" Тогда
			Если Вложения.Количество() = 1 Тогда
				УстановитьОшибкуВСтроке(Строка,
					НСтр("ru = 'Указана закрывающаяся скобка до открывающейся скобки'"));
			Иначе
				УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст, Строка);
			КонецЕсли;
		Иначе
			СтрокаДобавить(ТекущееВложение, Строка, Контекст);
		КонецЕсли;
	КонецЦикла;
	
	Пока Вложения.Количество() > 1 Цикл
		ИндексПоследнегоВложения = Вложения.Количество() - 1;
		Вложение = Вложения[ИндексПоследнегоВложения];
		Вложение.КонечнаяСтрока = Строка;
		Вложения.Удалить(ИндексПоследнегоВложения);
		УстановитьОшибкуВСтроке(Вложение,
			НСтр("ru = 'Указана открывающаяся скобка без закрывающейся скобки'"), Истина);
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Для процедуры РазобратьУсловие.
//
// Параметры:
//    Строки - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Функция ВыраженияВыборКогдаТогдаВоВложениях(Строки, Контекст)
	
	Результат = Новый Массив;
	
	ТекущееВложение = Новый Структура("Строки, Уточнение", Результат, "");
	Вложения = Новый Массив;
	Вложения.Добавить(ТекущееВложение);
	
	Для Каждого ОписаниеСтроки Из Строки Цикл
		Строка = СтрокаТаблицы(ОписаниеСтроки, Контекст);
		
		Если Строка.Вид <> "КлючевоеСлово"
		 Или Строка.Тип <> "СловоВыбора" Тогда
			
			Если ТекущееВложение.Уточнение = "Выбор" Тогда
				СтрокаДобавить(СтрокаТаблицы(ТекущееВложение.Строки[0], Контекст), Строка, Контекст);
			Иначе
				СтрокаДобавить(ТекущееВложение, Строка, Контекст);
				Если Строка.Символы = "(" Тогда
					Строка.Строки = ВыраженияВыборКогдаТогдаВоВложениях(Строка.Строки, Контекст);
				КонецЕсли;
			КонецЕсли;
		ИначеЕсли Строка.Уточнение = "Выбор" Тогда
			Если Вложения.Количество() = 1 Тогда
				// Стандартная обработка после условия.
			Иначе
				Если ТекущееВложение.Уточнение = "Выбор" Тогда
					УстановитьОшибкуВСтроке(ТекущееВложение, ПодставитьКлючевыеСловаВСтроку(Контекст,
						НСтр("ru = 'После ключевого слова ""%1"" не указано ключевое слова ""%2""'"), "Выбор,Когда"), Истина);
					ВосстановитьСтруктуруВыбора(ТекущееВложение, Вложения, ТекущееВложение, "Когда", Контекст);
				КонецЕсли;
			КонецЕсли;
			ДобавитьВложение(Строка, Вложения, ТекущееВложение, Контекст);
			ТекущееВложение.Строки.Добавить(ДополнительнаяСтрока(ТекущееВложение, "Выбор", Контекст));
			
		ИначеЕсли Строка.Уточнение = "Когда" Тогда
			
			Если Вложения.Количество() = 1 Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевого слова ""%2""'"), "Когда,Выбор"));
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Выбор", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Выбор" Тогда
				// Стандартная обработка после условия.
				
			ИначеЕсли ТекущееВложение.Уточнение = "Когда" Тогда
				УстановитьОшибкуВСтроке(ТекущееВложение, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'После ключевого слова ""%1"" не указано ключевое слова ""%2""'"), "Когда,Тогда"), Истина);
				ВосстановитьСтруктуруВыбора(ТекущееВложение, Вложения, ТекущееВложение, "Тогда", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Тогда" Тогда
				УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
				
			Иначе // ТекущееВложение.Уточнение = "Иначе"
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" должно быть до ключевого слова ""%2""'"), "Когда,Иначе"));
				УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
			КонецЕсли;
			ДобавитьВложение(Строка, Вложения, ТекущееВложение, Контекст);
			
		ИначеЕсли Строка.Уточнение = "Тогда" Тогда
			
			Если Вложения.Количество() = 1 Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевых слов ""%2"" и ""%3""'"), "Тогда,Выбор,Когда"));
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Выбор,Когда", Контекст);
			
			ИначеЕсли ТекущееВложение.Уточнение = "Выбор" Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевого слова ""%2""'"), "Тогда,Когда"));
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Когда", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Когда" Тогда
				// Стандартная обработка после условия.
				
			ИначеЕсли ТекущееВложение.Уточнение = "Тогда" Тогда
				УстановитьОшибкуВСтроке(ТекущееВложение, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'После ключевого слова ""%1"" не указано ключевое слова ""%2""'"), "Тогда,Когда"), Истина);
				ВосстановитьСтруктуруВыбора(ТекущееВложение, Вложения, ТекущееВложение, "Когда", Контекст);
				
			Иначе // ТекущееВложение.Уточнение = "Иначе"
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" должно быть до ключевого слова ""%2""'"), "Тогда,Иначе"));
				УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Когда", Контекст);
			КонецЕсли;
			УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
			ДобавитьВложение(Строка, Вложения, ТекущееВложение, Контекст);
			
		ИначеЕсли Строка.Уточнение = "Иначе" Тогда
			
			Если Вложения.Количество() = 1 Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевых слов ""%2"", ""%3"" и ""%4""'"), "Иначе,Выбор,Когда,Тогда"));
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Выбор,Когда,Тогда", Контекст);
			
			ИначеЕсли ТекущееВложение.Уточнение = "Выбор" Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевых слов ""%2"" и ""%3""'"), "Иначе,Когда,Тогда"));
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Когда,Тогда", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Когда" Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевого слова ""%2""'"), "Иначе,Тогда"));
				УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Тогда", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Тогда" Тогда
				// Стандартная обработка после условия.
				
			Иначе // ТекущееВложение.Уточнение = "Иначе"
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано повторно'"), "Иначе"));
			КонецЕсли;
			УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
			ДобавитьВложение(Строка, Вложения, ТекущееВложение, Контекст);
			
		Иначе // Строка.Уточнение = "Конец"
			
			Если Вложения.Количество() = 1 Тогда
				УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевых слов ""%2"", ""%3"", ""%4"" и ""%5""'"), "Конец,Выбор,Когда,Тогда,Иначе"));
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Выбор,Когда,Тогда,Иначе", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Выбор" Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевых слов ""%2"", ""%3"" и ""%4""'"), "Конец,Когда,Тогда,Иначе"));
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Когда,Тогда,Иначе", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Когда" Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевых слов ""%2"" и ""%3""'"), "Конец,Тогда,Иначе"));
				УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Тогда,Иначе", Контекст);
				
			ИначеЕсли ТекущееВложение.Уточнение = "Тогда" Тогда
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(Контекст,
					НСтр("ru = 'Ключевое слово ""%1"" указано до ключевого слова ""%2""'"), "Конец,Иначе"));
				УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
				ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, "Иначе", Контекст);
				
			Иначе // ТекущееВложение.Уточнение = "Иначе".
				// Стандартная обработка после условия.
			КонецЕсли;
			УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
			УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
		КонецЕсли;
	КонецЦикла;
	
	Пока Вложения.Количество() > 1 Цикл
		ТекущееВложение = Вложения[Вложения.Количество() - 1];
		
		Если ТекущееВложение.Уточнение = "Выбор" Тогда
			ТекстОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После ключевого слова ""%1"" не указаны ключевые слова ""%2"", ""%3"", ""%4"" и ""%5""'"), "Выбор,Когда,Тогда,Иначе,Конец");
			ВосстановитьСтруктуруВыбора(ТекущееВложение, Вложения, ТекущееВложение, "Когда,Тогда,Иначе", Контекст);
			
		ИначеЕсли ТекущееВложение.Уточнение = "Когда" Тогда
			ТекстОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После ключевого слова ""%1"" не указаны ключевые слова ""%2"", ""%3"", ""%4""'"), "Когда,Тогда,Иначе,Конец");
			УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
			ВосстановитьСтруктуруВыбора(ТекущееВложение, Вложения, ТекущееВложение, "Тогда,Иначе", Контекст);
			
		ИначеЕсли ТекущееВложение.Уточнение = "Тогда" Тогда
			ТекстОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После ключевого слова ""%1"" не указаны ключевые слова ""%2"" и ""%3""'"), "Тогда,Иначе,Конец");
			УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
			ВосстановитьСтруктуруВыбора(ТекущееВложение, Вложения, ТекущееВложение, "Иначе", Контекст);
			
		Иначе // ТекущееВложение.Уточнение = "Иначе"
			ТекстОшибки = ПодставитьКлючевыеСловаВСтроку(Контекст,
				НСтр("ru = 'После ключевого слова ""%1"" не указано ключевое слово ""%2""'"), "Иначе,Конец");
		КонецЕсли;
		УстановитьОшибкуВСтроке(ТекущееВложение, ТекстОшибки, Истина);
		УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
		УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст);
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Для процедуры РазобратьУсловие.
//
// Параметры:
//    Строки - Массив из см. СтрокаТаблицы.ОписаниеСтроки
//
Функция ФункцииСВыражениямиВСкобках(Строки, ВнутренниеДанные)
	
	Результат = Новый Массив;
	СтрокаРезультата = Новый Структура("Строки", Результат);
	
	КоличествоСтрок = Строки.Количество();
	
	Строка = Неопределено;
	Индекс = 0;
	Пока Индекс < КоличествоСтрок Цикл
		ПредыдущаяСтрока = Строка; // СтрокаТаблицыЗначений из см. ТаблицаНаборовСимволов
		Строка = СтрокаТаблицы(Строки[Индекс], ВнутренниеДанные);
		
		Если Строка.Символы = "(" Тогда
			Строка.Строки = ФункцииСВыражениямиВСкобках(Строка.Строки, ВнутренниеДанные);
			
		ИначеЕсли Строка.Вид = "КлючевоеСлово"
		        И (    Строка.Тип = "Функция"
		           Или Строка.Уточнение = "В" ) Тогда
			
			СледующаяСтрока = ?(Индекс + 1 < Строки.Количество(),
				СтрокаТаблицы(Строки[Индекс + 1], ВнутренниеДанные), Неопределено);
				
			Если Индекс + 1 < Строки.Количество()
			   И СледующаяСтрока.Символы = "(" Тогда
				
				Индекс = Индекс + 1;
				Строка.Строки = ФункцииСВыражениямиВСкобках(СледующаяСтрока.Строки, ВнутренниеДанные);
				Строка.КонечнаяСтрока = СледующаяСтрока.КонечнаяСтрока;
				
				Если Строка.Строки.Количество() = 0 Тогда
					Если Строка.Тип = "Функция" Тогда
						УстановитьОшибкуВСтроке(Строки[Индекс], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'У функции ""%1"" не указано ни одного параметра'"), Строка.Символы), Истина);
					Иначе
						УстановитьОшибкуВСтроке(Строки[Индекс], СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'В списке значений операции ""%1"" не указано ни одного значения'"), Строка.Символы), Истина);
					КонецЕсли;
				КонецЕсли;
			Иначе
				Строка.Строки = Новый Массив;
				Если Строка.Тип = "Функция" Тогда
					УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'После функции ""%1"" не указаны параметры в скобках'"), Строка.Символы), Истина);
				Иначе
					УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'После ключевого слова ""%1"" не указаны значения в скобках'"), Строка.Символы), Истина);
				КонецЕсли;
			КонецЕсли;
			
		ИначеЕсли Строка.Вид = "КлючевоеСлово"
		        И Строка.Уточнение = "Есть"  Тогда
			
			СледующаяСтрока = ?(Индекс + 1 < Строки.Количество(),
				СтрокаТаблицы(Строки[Индекс + 1], ВнутренниеДанные), Неопределено);
			
			Если Индекс + 1 < Строки.Количество()
			   И СледующаяСтрока.Вид = "КлючевоеСлово"
			   И СледующаяСтрока.Уточнение = "Null" Тогда
				
				Индекс = Индекс + 1;
				СтрокаДобавить(Строка, СледующаяСтрока, ВнутренниеДанные);
			Иначе
				Строка.Строки = Новый Массив;
				УстановитьОшибкуВСтроке(Строка, ПодставитьКлючевыеСловаВСтроку(ВнутренниеДанные,
						НСтр("ru = 'После ключевого слова ""%2"" не указано ключевое слово ""%1""'"),
						"Null",
						Строка.Символы),
					Истина);
			КонецЕсли;
			
		ИначеЕсли Строка.Вид = "Имя"
		        И СтрНачинаетсяС(Строка.Символы, ".")
		        И ПредыдущаяСтрока <> Неопределено Тогда
			
			Если ПредыдущаяСтрока <> Неопределено
			   И ПредыдущаяСтрока.Вид = "КлючевоеСлово"
			   И ПредыдущаяСтрока.Уточнение = "Выразить" Тогда
				
				Строка.Тип = "ДополнениеКВыразить";
				СтрокаДобавить(ПредыдущаяСтрока, Строка, ВнутренниеДанные);
				Индекс = Индекс + 1;
				Продолжить;
			Иначе
				УстановитьОшибкуВСтроке(Строка, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Имя поля не может начинаться с символа "".""'"), Строка.Символы));
			КонецЕсли;
		КонецЕсли;
		
		СтрокаДобавить(СтрокаРезультата, Строка, ВнутренниеДанные);
		Индекс = Индекс + 1;
	КонецЦикла;
	
	Возврат Результат;
	
КонецФункции

// Для функций ВыраженияВСкобкахВоВложениях, ВыраженияВыборКогдаТогдаВоВложениях.
Процедура ДобавитьВложение(Строка, Вложения, ТекущееВложение, Контекст)
	
	СтрокаДобавить(ТекущееВложение, Строка, Контекст);
	ТекущееВложение = Строка;
	Вложения.Добавить(ТекущееВложение);
	
КонецПроцедуры

// Для функций ВыраженияВСкобкахВоВложениях, ВыраженияВыборКогдаТогдаВоВложениях.
Процедура УдалитьПоследнееВложение(Вложения, ТекущееВложение, Контекст, КонечнаяСтрока = Неопределено)
	
	Если КонечнаяСтрока = Неопределено Тогда
		Если ТекущееВложение.Строки.Количество() = 0 Тогда
			КонечнаяСтрока = ТекущееВложение;
		Иначе
			КонечнаяСтрока = СтрокаТаблицы(ТекущееВложение.Строки[ТекущееВложение.Строки.Количество() - 1], Контекст);
			Если КонечнаяСтрока.КонечнаяСтрока <> Неопределено Тогда
				КонечнаяСтрока = СтрокаТаблицы(КонечнаяСтрока.КонечнаяСтрока, Контекст);
			КонецЕсли;
		КонецЕсли;
	КонецЕсли;
	
	ТекущееВложение.КонечнаяСтрока = Контекст.ТаблицаНаборовСимволов.Индекс(КонечнаяСтрока);
	
	ИндексПоследнегоВложения = Вложения.Количество() - 1;
	Вложения.Удалить(ИндексПоследнегоВложения);
	ТекущееВложение = Вложения[ИндексПоследнегоВложения - 1];
	
КонецПроцедуры

// Для функции ВыраженияВыборКогдаТогдаВоВложениях.
Процедура ВосстановитьСтруктуруВыбора(Строка, Вложения, ТекущееВложение, СписокНедостающихСлов, Контекст)
	
	НедостающиеСлова = СтрРазделить(СписокНедостающихСлов, ",", Ложь);
	
	Для Каждого НедостающееСлово Из НедостающиеСлова Цикл
		НоваяСтрока = ДополнительнаяСтрока(Строка, НедостающееСлово, Контекст);
		ТекущееВложение.Строки.Добавить(НоваяСтрока);
		
		Если НедостающееСлово = "Выбор" Тогда
			ТекущееВложение = НоваяСтрока;
			Вложения.Добавить(ТекущееВложение);
		КонецЕсли;
	КонецЦикла;
	
	Если НедостающееСлово <> "Выбор" Тогда
		ТекущееВложение = НоваяСтрока;
		Вложения.Добавить(ТекущееВложение);
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ОбработатьПропущеннуюЛогическуюОперацию, ВыраженияВыборКогдаТогдаВоВложениях,
// ВосстановитьСтруктуруВыбора и для функции ПараметрыРазделенныеЗапятыми.
//
// Возвращаемое значение:
//   Структура:
//   * Символы - Строка
//   * Вид - Строка
//   * Тип - Строка
//   * Приоритет - Число
//   * Уточнение - Строка
//   * Строки - Массив из Число - индексы строк ТаблицаНаборовСимволов
//   * КонечнаяСтрока - Число - индекс строки ТаблицаНаборовСимволов
//   * ПозицияОшибки - Число
//   * ТекстОшибки - Строка
//
Функция ДополнительнаяСтрока(Строка, Уточнение = "", Контекст = Неопределено)
	
	НоваяСтрока = Новый Структура;
	НоваяСтрока.Вставить("Символы");
	НоваяСтрока.Вставить("Вид");
	НоваяСтрока.Вставить("Тип");
	НоваяСтрока.Вставить("Приоритет");
	НоваяСтрока.Вставить("Уточнение", Уточнение);
	НоваяСтрока.Вставить("Строки", Новый Массив);
	НоваяСтрока.Вставить("КонечнаяСтрока");
	НоваяСтрока.Вставить("ПозицияОшибки");
	НоваяСтрока.Вставить("ТекстОшибки");
	
	СвойстваСлова = ?(Контекст = Неопределено,
		Неопределено, Контекст.СинтаксисЯзыка.СловаЯзыка.Получить(ВРег(Уточнение)));
	
	Если СвойстваСлова <> Неопределено Тогда
		НоваяСтрока.Вид       = "КлючевоеСлово";
		НоваяСтрока.Тип       = СвойстваСлова.Тип;
		НоваяСтрока.Приоритет = СвойстваСлова.Приоритет;
	КонецЕсли;
	
	Возврат НоваяСтрока;
	
КонецФункции

// Для процедур РазобратьДополнительныеТаблицы, РазобратьУсловиеОграничения.
//
// Параметры:
//    СтрокиЧасти - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Процедура ИзменитьВидКлючевогоСловаСписокНаИмя(СтрокиЧасти, ИсключаемаяСтрока = Неопределено)
	
	Для Каждого Строка Из СтрокиЧасти Цикл
		Если Строка = ИсключаемаяСтрока
		 Или Строка.Вид <> "КлючевоеСлово"
		 Или Строка.Уточнение <> "ЭтотСписок" Тогда
			Продолжить;
		КонецЕсли;
		Строка.Вид = "Имя";
		Строка.Уточнение = "";
	КонецЦикла;
	
КонецПроцедуры

// Для процедур РазобратьДополнительныеТаблицы, РазобратьСоединение.
//
// Параметры:
//    СтрокиЧасти - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Процедура УстановитьПсевдоним(СтрокаЧасти, ОписаниеСоединения, ВнутренниеДанные)
	
	Если ЗначениеЗаполнено(СтрокаЧасти.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	ПозицияТочки = СтрНайти(СтрокаЧасти.Символы, ".");
	Если ПозицияТочки > 0 Тогда
		СтрокаЧасти.ПозицияОшибки = ПозицияТочки - 1;
		СтрокаЧасти.ТекстОшибки =
			НСтр("ru = 'Псевдоним не может содержать символа "".""'");
			
	ИначеЕсли ТипЗнч(ОписаниеСоединения) = Тип("Строка") Тогда
		ОписаниеСоединения = СтрокаЧасти.Символы;
		ВнутренниеДанные.Псевдонимы.Вставить(ВРег(СтрокаЧасти.Символы),
			Новый Структура("Псевдоним, Таблица", СтрокаЧасти.Символы));
	Иначе
		ОписаниеСоединения.Псевдоним = СтрокаЧасти.Символы + "Псевдоним";
		Если ВнутренниеДанные.Псевдонимы.Получить(ВРег(СтрокаЧасти.Символы)) = Неопределено Тогда
			Если ЗначениеЗаполнено(ОписаниеСоединения.Таблица) Тогда
				ВнутренниеДанные.Псевдонимы.Вставить(ВРег(СтрокаЧасти.Символы),
					Новый Структура("Псевдоним, Таблица",
						ОписаниеСоединения.Псевдоним, ОписаниеСоединения.Таблица));
			КонецЕсли;
		Иначе
			СтрокаЧасти.ТекстОшибки = НСтр("ru = 'Псевдоним повторяется'");
		КонецЕсли;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры РазобратьСоединение.
//
// Параметры:
//    СтрокиЧасти - Массив из СтрокаТаблицыЗначений: см. ТаблицаНаборовСимволов
//
Процедура УстановитьИмяТаблицы(СтрокаЧасти, ОписаниеСоединения, ВнутренниеДанные)
	
	Если ЗначениеЗаполнено(СтрокаЧасти.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	Если СтрНачинаетсяС(СтрокаЧасти.Символы, ".") Тогда
		СтрокаЧасти.ТекстОшибки =
			НСтр("ru = 'Имя таблицы не может начинаться с символа "".""'");
		Возврат;
	КонецЕсли;
	
	Если СтрЧислоВхождений(СтрокаЧасти.Символы, ".") > 2 Тогда
		
		ПозицияТочки = СтрНайти(СтрокаЧасти.Символы, ".");
		ПозицияТочки = ПозицияТочки + СтрНайти(Сред(СтрокаЧасти.Символы, ПозицияТочки + 1), ".");
		
		ПозицияТочки = ПозицияТочки + СтрНайти(Сред(СтрокаЧасти.Символы, ПозицияТочки + 1), ".");
		СтрокаЧасти.ТекстОшибки =
			НСтр("ru = 'Полное имя таблицы не может содержать более двух символов "".""'");
		
		СтрокаЧасти.ПозицияОшибки = ПозицияТочки - 1;
		Возврат;
	КонецЕсли;
	
	ОписаниеСоединения.Таблица = СтрокаЧасти.Символы;
	
	ДобавитьТребуемуюТаблицуКакИсточникДанных(ВнутренниеДанные, ОписаниеСоединения.Таблица, СтрокаЧасти);
	
КонецПроцедуры

// Для процедуры УстановитьИмяТаблицы.
Процедура ДобавитьТребуемуюТаблицуКакИсточникДанных(Контекст, Таблица, Источник)
	
	Если ЗначениеЗаполнено(Источник.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	СвойстваИмени = СвойстваИмениТаблицы(Контекст, Таблица);
	
	Если СвойстваИмени.ЧислоЧастейИмени < 2
	 Или СвойстваИмени.ЧислоЧастейИмени > 3 Тогда
		УстановитьОшибкуВСтроке(Источник,
			НСтр("ru = 'В имени присоединяемой таблицы должна быть одна или две точки'"));
		Возврат;
	КонецЕсли;
	
	Если СвойстваИмени.СвойстваТипаТаблиц = Неопределено Тогда
		УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректное начало ""%1"" имени таблицы ""%2""'"), СвойстваИмени.ИмяТипа, Таблица));
		Возврат;
	КонецЕсли;
	
	Если СвойстваИмени.ЧислоЧастейИмени = 3 Тогда
		СвойстваУточнения = СвойстваИмени.СвойстваТипаТаблиц.УточнениеТаблиц.Получить(
			ВРег(СвойстваИмени.Расширение));
		
		Если СвойстваУточнения <> Неопределено
		   И СвойстваУточнения.Использование <> "Разрешено" Тогда
			
			Если СвойстваУточнения.Использование = "Недопустимо" Тогда
				УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Недопустимо использовать таблицы ""%1"" группы таблиц ""%2""'"),
					СвойстваИмени.Расширение, СвойстваИмени.ИмяТипа));
			Иначе
				УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Запрещено присоединять таблицы ""%1"" группы таблиц ""%2""'"),
					СвойстваИмени.Расширение, СвойстваИмени.ИмяТипа));
			КонецЕсли;
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	Свойства = СвойстваТребуемойТаблицы(Контекст, СвойстваИмени);
	Свойства.Источники.Добавить(Источник);
	
КонецПроцедуры

// Для процедур ВыделитьПсевдонимПоля, РазобратьПервыйПараметрПроверочнойФункции,
// РазобратьДополнительныйПараметрПроверочнойФункции, РазобратьПараметрыФункцииЗначениеИлиФункцииТип.
//
Процедура ДобавитьТребуемуюТаблицуКакСсылочныйТип(Контекст, Таблица, Источник)
	
	Если ЗначениеЗаполнено(Источник.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	СвойстваИмени = СвойстваИмениТаблицы(Контекст, Таблица);
	
	Если СвойстваИмени.ЧислоЧастейИмени <> 2 Тогда
		УстановитьОшибкуВСтроке(Источник,
			НСтр("ru = 'В имени таблицы, указанной в качестве типа, должна быть одна точка'"));
		Возврат;
	КонецЕсли;
	
	Если СвойстваИмени.СвойстваТипаТаблиц = Неопределено Тогда
		УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректное начало ""%1"" имени таблицы ""%2""'"), СвойстваИмени.ИмяТипа, Таблица));
		Возврат;
	КонецЕсли;
	
	Если Не СвойстваИмени.СвойстваТипаТаблиц.ЭтоСсылочныйТип Тогда
		УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Группа таблиц ""%1"" не входит в состав ссылочных типов'"), СвойстваИмени.ИмяТипа, Таблица));
		Возврат;
	КонецЕсли;
	
	Свойства = СвойстваТребуемойТаблицы(Контекст, СвойстваИмени);
	Свойства.Источники.Добавить(Источник);
	
КонецПроцедуры

// Для процедуры РазобратьПараметрыФункцииЗначениеИлиФункцииТип.
Процедура ДобавитьТребуемыйПредопределенныйЭлемент(Контекст, ПолноеИмяПредопределенного, Источник)
	
	Если ЗначениеЗаполнено(Источник.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	СвойстваИмени = СвойстваИмениТаблицы(Контекст, ПолноеИмяПредопределенного);
	
	Если СвойстваИмени.ЧислоЧастейИмени <> 3 Тогда
		УстановитьОшибкуВСтроке(Источник,
			НСтр("ru = 'В имени предопределенного значения должно быть две точки'"));
		Возврат;
	КонецЕсли;
	
	Если СвойстваИмени.СвойстваТипаТаблиц = Неопределено Тогда
		УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Некорректное начало ""%1"" имени таблицы ""%2""'"),
			СвойстваИмени.ИмяТипа,
			СвойстваИмени.ИмяТипа + "." + СвойстваИмени.ИмяБезТипа));
		Возврат;
	КонецЕсли;
	
	Если Не СвойстваИмени.СвойстваТипаТаблиц.ЭтоСсылочныйТип Тогда
		УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Группа таблиц ""%1"" не входит в состав ссылочных типов'"), СвойстваИмени.ИмяТипа));
		Возврат;
	КонецЕсли;
	
	СвойстваСлова = Контекст.СинтаксисЯзыка.СловаЯзыка.Получить(ВРег(СвойстваИмени.Расширение)); // См. СвойстваСлова
	
	Если Не СвойстваИмени.СвойстваТипаТаблиц.ЕстьПредопределенные
	   И Не СвойстваИмени.ИмяТипа = "Перечисление"
	   И (    СвойстваСлова = Неопределено
	      Или СвойстваСлова.Идентификатор <> "ПустаяСсылка") Тогда
		
		УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В группе таблиц ""%1"" нет предопределенных элементов'"), СвойстваИмени.ИмяТипа));
		Возврат;
	КонецЕсли;
	
	Свойства = СвойстваТребуемойТаблицы(Контекст, СвойстваИмени, Истина);
	
	СвойстваПредопределенного = Свойства.Предопределенные.Получить(ВРег(СвойстваИмени.Расширение));
	Если СвойстваПредопределенного = Неопределено Тогда
		СвойстваПредопределенного = НовыеСвойстваПредопределенного();
		СвойстваПредопределенного.Вставить("ИмяСуществует", Ложь);
		СвойстваПредопределенного.Вставить("Источники", Новый Массив);
		Свойства.Предопределенные.Вставить(ВРег(СвойстваИмени.Расширение), СвойстваПредопределенного);
	КонецЕсли;
	СвойстваПредопределенного.Источники.Добавить(Источник);
	
КонецПроцедуры

// Для процедуры РазобратьПараметрыФункцииРольДоступна.
Процедура ПроверитьИмяРоли(ИмяРоли, Источник)
	
	Если ЗначениеЗаполнено(Источник.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	Если СтрНайти(ИмяРоли, ".") > 0 Тогда
		УстановитьОшибкуВСтроке(Источник,
			НСтр("ru = 'В имени роли не должно быть точек'"));
		Возврат;
	КонецЕсли;
	
	Если Метаданные.Роли.Найти(ИмяРоли) = Неопределено Тогда
		УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Роль ""%1"" отсутствует в метаданных'"), ИмяРоли));
		Возврат;
	КонецЕсли;
	
КонецПроцедуры

Процедура ПроверитьИмяПраваОбъектаМетаданных(ИмяПрава, ИсточникПрава, ИмяОбъекта, ИсточникОбъекта)
	
	Если ЗначениеЗаполнено(ИсточникПрава.ТекстОшибки)
	 Или ИсточникОбъекта <> Неопределено
	   И ЗначениеЗаполнено(ИсточникОбъекта.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	Если СтрНайти(ИмяПрава, ".") > 0 Тогда
		УстановитьОшибкуВСтроке(ИсточникПрава,
			НСтр("ru = 'В имени права не должно быть точек'"));
		Возврат;
	КонецЕсли;
	
	ИмяСтандартногоРеквизита = Неопределено;
	ОбъектМетаданных = ОбъектМетаданныхПоПолномуИмениДляПроверкиПрава(ИмяОбъекта, ИмяСтандартногоРеквизита);
	Если ОбъектМетаданных = Неопределено Тогда
		УстановитьОшибкуВСтроке(ИсточникОбъекта, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Объект метаданных ""%1"" не существует'"), ИмяОбъекта));
		Возврат;
	КонецЕсли;
	
	ТекстОшибки = "";
	Попытка
		ПравоДоступа(ИмяПрава, ОбъектМетаданных, , ИмяСтандартногоРеквизита);
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке);
	КонецПопытки;
	
	Если ЗначениеЗаполнено(ТекстОшибки) Тогда
		УстановитьОшибкуВСтроке(ИсточникПрава, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось проверить право ""%1"" объекта метаданных ""%2"" по причине:
			           |%3'"), ИмяПрава, ИмяОбъекта, ТекстОшибки));
		Возврат;
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ПроверитьИмяПраваОбъектаМетаданных
Функция ОбъектМетаданныхПоПолномуИмениДляПроверкиПрава(ПолноеИмя, ИмяСтандартногоРеквизита = Неопределено)
	
	ОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ПолноеИмя);
	Если ОбъектМетаданных <> Неопределено Тогда
		Возврат ОбъектМетаданных;
	КонецЕсли;
	
	ЧастиИмени = СтрРазделить(ПолноеИмя, ".");
	Если ЧастиИмени.Количество() < 4 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ИмяОсновногоОбъектаМетаданных = ЧастиИмени[0] + "." + ЧастиИмени[1];
	ОсновнойОбъектМетаданных = ОбщегоНазначения.ОбъектМетаданныхПоПолномуИмени(ИмяОсновногоОбъектаМетаданных);
	Если ОсновнойОбъектМетаданных = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Если ВРег(ЧастиИмени[2]) = ВРег("СтандартнаяТабличнаяЧасть") // @Non-NLS
	 Или ВРег(ЧастиИмени[2]) = ВРег("StandardTabularSection") Тогда
		
		Свойства = Новый Структура("СтандартныеТабличныеЧасти");
		ЗаполнитьЗначенияСвойств(Свойства, ОсновнойОбъектМетаданных);
		Если Свойства.СтандартныеТабличныеЧасти = Неопределено Тогда
			Возврат Неопределено;
		КонецЕсли;
		ИмяСтандартнойТабличнойЧасти = ЧастиИмени[3];
		Для Каждого СтандартнаяТабличнаяЧасть Из Свойства.СтандартныеТабличныеЧасти Цикл
			Если ВРег(СтандартнаяТабличнаяЧасть.Имя) = ВРег(ИмяСтандартнойТабличнойЧасти) Тогда
				СтандартныеРеквизиты = СтандартнаяТабличнаяЧасть.СтандартныеРеквизиты;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если СтандартныеРеквизиты = Неопределено
		 Или ЧастиИмени.Количество() <> 6 Тогда
			Возврат Неопределено;
		КонецЕсли;
		ЧастиИмени.Удалить(0);
		ЧастиИмени.Удалить(0);
		ВладелецСтандартныхРеквизитов = СтандартнаяТабличнаяЧасть;
	Иначе
		ВладелецСтандартныхРеквизитов = ОсновнойОбъектМетаданных;
	КонецЕсли;
	
	Если ВРег(ЧастиИмени[2]) <> ВРег("СтандартныйРеквизит") // @Non-NLS
	   И ВРег(ЧастиИмени[2]) <> ВРег("StandardAttribute") Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	Свойства = Новый Структура("СтандартныеРеквизиты");
	ЗаполнитьЗначенияСвойств(Свойства, ВладелецСтандартныхРеквизитов);
	Если Свойства.СтандартныеРеквизиты = Неопределено Тогда
		Возврат Неопределено;
	КонецЕсли;
	ИмяСтандартногоРеквизита = ЧастиИмени[3];
	СтандартныеРеквизиты = Свойства.СтандартныеРеквизиты; // ОписанияСтандартныхРеквизитов
	
	Для Каждого СтандартныйРеквизит Из СтандартныеРеквизиты Цикл
		Если ВРег(СтандартныйРеквизит.Имя) = ВРег(ИмяСтандартногоРеквизита) Тогда
			Если ЗначениеЗаполнено(ИмяСтандартнойТабличнойЧасти) Тогда
				ИмяСтандартногоРеквизита = ИмяСтандартнойТабличнойЧасти + "." + ИмяСтандартногоРеквизита;
			КонецЕсли;
			Возврат ОсновнойОбъектМетаданных;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Неопределено;
	
КонецФункции

// Для процедур ОтметитьНекорректныеАргументыИЗапрещенныеУзлы, ВыделитьПсевдонимПоля,
// ДобавитьТипыВидовДоступаПользователиИВнешниеПользователиДляПроверкиОтсутствия.
//
Процедура ДобавитьТребуемоеПолеТаблицы(Контекст, Таблица, ИмяПоля, Источник,
			ТипПоля = "", ИсточникТипаПоля = Неопределено, УзелПоле = Неопределено)
	
	Если ЗначениеЗаполнено(Источник.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	СвойстваИмени = СвойстваИмениТаблицы(Контекст, Таблица);
	Если СвойстваИмени.СвойстваТипаТаблиц = Неопределено Тогда
		
		Если СвойстваИмени.ЭтоОсновнаяТаблица
		   И Не Контекст.Свойство("ОшибкаНаПервоеПолеОсновнойТаблицыУстановлена") Тогда
			
			Контекст.Вставить("ОшибкаНаПервоеПолеОсновнойТаблицыУстановлена");
			УстановитьОшибкуВСтрокеИмениПоля(Контекст, Источник,
				НСтр("ru = 'Поле не существует, так как указана несуществующая таблица ""%1""'"), 0, ,
				Контекст.ОсновнаяТаблица);
		КонецЕсли;
		
		Возврат;
	КонецЕсли;
	
	Свойства = СвойстваТребуемойТаблицы(Контекст, СвойстваИмени);
	
	СвойстваПоля = Свойства.Поля.Получить(ВРег(ИмяПоля));
	Если СвойстваПоля = Неопределено Тогда
		СвойстваПоля = НовыеСвойстваПоля();
		СвойстваПоля.Вставить("ПолеСОшибкой", 0);
		СвойстваПоля.Вставить("ВидОшибки",    "");
		СвойстваПоля.Вставить("Коллекция",    "");
		СвойстваПоля.Вставить("СодержитТипы", Новый Соответствие);
		СвойстваПоля.Вставить("Источники",    Новый Соответствие);
		СвойстваПоля.Вставить("УзлыПоле",     Новый Массив);
		Свойства.Поля.Вставить(ВРег(ИмяПоля), СвойстваПоля);
		
		Если Свойства.Свойство("ПервоеПоле")
		   И Свойства.ПервоеПоле = Неопределено
		   И Источник <> Неопределено Тогда
			
			СвойстваПоля.Вставить("ПервыйИсточник", Новый Структура("Ключ,Значение", Источник, Таблица));
			Свойства.ПервоеПоле = СвойстваПоля;
		КонецЕсли;
	КонецЕсли;
	СвойстваПоля.Источники.Вставить(Источник, Таблица);
	Если УзелПоле <> Неопределено Тогда
		СвойстваПоля.УзлыПоле.Добавить(УзелПоле);
	КонецЕсли;
	
	Если Не ЗначениеЗаполнено(ТипПоля) Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(ИсточникТипаПоля) = Тип("СтрокаТаблицыЗначений")
	   И ЗначениеЗаполнено(ИсточникТипаПоля.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	СвойстваТипа = СвойстваПоля.СодержитТипы.Получить(ВРег(ТипПоля));
	Если СвойстваТипа = Неопределено Тогда
		СоставИмени = СтрРазделить(ТипПоля, ".");
		СвойстваТипаТаблиц = Контекст.СинтаксисЯзыка.ТипыТаблиц.ПоИменам.Получить(ВРег(СоставИмени[0]));
		Если СвойстваТипаТаблиц = Неопределено Тогда
			ИмяТипа = ТипПоля;
			ИмяКоллекцииТипа = "";
			ИмяОбъектаКоллекцииТипа = "";
		Иначе
			ИмяТипа = СвойстваТипаТаблиц.ЯзыкРусский + "Ссылка." + СоставИмени[1]; // @Non-NLS
			ИмяКоллекцииТипа = СвойстваТипаТаблиц.ИмяКоллекции;
			ИмяОбъектаКоллекцииТипа = СоставИмени[1];
		КонецЕсли;
		СвойстваТипа = Новый Структура;
		СвойстваТипа.Вставить("СодержитТип", Ложь);
		СвойстваТипа.Вставить("ИмяТипа", ИмяТипа);
		СвойстваТипа.Вставить("ИмяКоллекцииТипа", ИмяКоллекцииТипа);
		СвойстваТипа.Вставить("ИмяОбъектаКоллекцииТипа", ИмяОбъектаКоллекцииТипа);
		СвойстваТипа.Вставить("Источники", Новый Соответствие);
		СвойстваПоля.СодержитТипы.Вставить(ВРег(ТипПоля), СвойстваТипа);
	КонецЕсли;
	СвойстваТипа.Источники.Вставить(ИсточникТипаПоля, Источник);
	
КонецПроцедуры

// Для процедур ДобавитьТребуемуюТаблицуКакИсточникДанных, ДобавитьТребуемуюТаблицуКакСсылочныйТип,
// ДобавитьТребуемыйПредопределенныйЭлемент, ДобавитьТребуемоеПолеТаблицы.
//
Функция СвойстваИмениТаблицы(Контекст, ПолноеИмя)
	
	Если ЗначениеЗаполнено(ПолноеИмя) Тогда
		Таблица = ПолноеИмя;
	Иначе
		Таблица = Контекст.ОсновнаяТаблица;
	КонецЕсли;
	
	СоставИмени = СтрРазделить(Таблица, ".", Ложь);
	
	Свойства = Новый Структура;
	Свойства.Вставить("ЧислоЧастейИмени", СоставИмени.Количество());
	Свойства.Вставить("ИмяТипа",    СоставИмени[0]);
	Свойства.Вставить("ИмяБезТипа", ?(СоставИмени.Количество() > 1, СоставИмени[1], Неопределено));
	Свойства.Вставить("Расширение", ?(СоставИмени.Количество() = 3, СоставИмени[2], Неопределено));
	Свойства.Вставить("ЭтоОсновнаяТаблица", ВРег(Таблица) = ВРег(Контекст.ОсновнаяТаблица));
	Свойства.Вставить("СвойстваТипаТаблиц",
		Контекст.СинтаксисЯзыка.ТипыТаблиц.ПоИменам.Получить(ВРег(СоставИмени[0])));
	
	Возврат Свойства;
	
КонецФункции

// Для процедур ДобавитьТребуемуюТаблицуКакИсточникДанных, ДобавитьТребуемуюТаблицуКакСсылочныйТип,
// ДобавитьТребуемыйПредопределенныйЭлемент, ДобавитьТребуемоеПолеТаблицы.
//
Функция СвойстваТребуемойТаблицы(Контекст, СвойстваИмени, БезРасширения = Ложь)
	
	ИмяКоллекции = СвойстваИмени.СвойстваТипаТаблиц.ИмяКоллекции;
	
	СоставКоллекции = Контекст.ПоляТаблиц.Получить(ИмяКоллекции);
	Если СоставКоллекции = Неопределено Тогда
		СоставКоллекции = НовыйСоставКоллекции();
		Контекст.ПоляТаблиц.Вставить(ИмяКоллекции, СоставКоллекции);
	КонецЕсли;
	
	Свойства = СоставКоллекции.Получить(ВРег(СвойстваИмени.ИмяБезТипа));
	Если Свойства = Неопределено Тогда
		Свойства = НовыеСвойстваТаблицы();
		Свойства.Вставить("ТаблицаСуществует",  Ложь);
		Свойства.Вставить("ЭтоОсновнаяТаблица", СвойстваИмени.ЭтоОсновнаяТаблица);
		Свойства.Вставить("Источники",          Новый Массив);
		Свойства.Вставить("Поля",               Новый Соответствие);
		Свойства.Вставить("Предопределенные",   Новый Соответствие);
		Свойства.Вставить("Расширения",         Новый Соответствие);
		Если СвойстваИмени.ЭтоОсновнаяТаблица И СвойстваИмени.Расширение = Неопределено Тогда
			Свойства.Вставить("ПервоеПоле");
		КонецЕсли;
		СоставКоллекции.Вставить(ВРег(СвойстваИмени.ИмяБезТипа), Свойства);
	КонецЕсли;
	
	Если СвойстваИмени.Расширение = Неопределено Или БезРасширения Тогда
		Возврат Свойства;
	КонецЕсли;
	
	СвойстваРасширения = Свойства.Расширения.Получить(ВРег(СвойстваИмени.Расширение));
	Если СвойстваРасширения = Неопределено Тогда
		СвойстваРасширения = НовыеСвойстваРасширения();
		СвойстваРасширения.Вставить("ТаблицаСуществует", Ложь);
		СвойстваРасширения.Вставить("Источники",         Новый Массив);
		СвойстваРасширения.Вставить("Поля",              Новый Соответствие);
		Если СвойстваИмени.ЭтоОсновнаяТаблица Тогда
			СвойстваРасширения.Вставить("ПервоеПоле");
		КонецЕсли;
		Свойства.Расширения.Вставить(ВРег(СвойстваИмени.Расширение), СвойстваРасширения);
	КонецЕсли;
	
	Возврат СвойстваРасширения;
	
КонецФункции

// Для процедур и функций ЧастиОграничения, РазобратьДополнительныеТаблицы, РазобратьСоединение,
// РазобратьУсловиеОграничения, ВыраженияВыборКогдаТогдаВоВложениях.
//
Функция ПодставитьКлючевыеСловаВСтроку(Контекст, Строка, СписокСлов, ПараметрОдин = "", ПараметрДва = "", ПараметрТри = "")
	
	Слова = СтрРазделить(СписокСлов, ",", Ложь);
	СловаДляПодстановки = Новый Соответствие;
	
	Для Каждого Слово Из Слова Цикл
		СловаДляПодстановки.Вставить(Слова.Найти(Слово),
			КлючевоеСловоСУчетомЯзыка(СокрЛП(Слово), Контекст));
	КонецЦикла;
	
	Индекс = СловаДляПодстановки.Количество();
	СловаДляПодстановки.Вставить(Индекс,     ПараметрОдин);
	СловаДляПодстановки.Вставить(Индекс + 1, ПараметрДва);
	СловаДляПодстановки.Вставить(Индекс + 2, ПараметрТри);
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Строка,
		СловаДляПодстановки[0], СловаДляПодстановки[1], СловаДляПодстановки[2],
		СловаДляПодстановки[3], СловаДляПодстановки[4], СловаДляПодстановки[5],
		СловаДляПодстановки[6], СловаДляПодстановки[7], СловаДляПодстановки[8]);
	
КонецФункции

// Для процедур РазобратьДополнительныеТаблицы, РазобратьСоединение, РазобратьУсловиеОграничения.
Функция КлючевоеСловоСУчетомЯзыка(ИдентификаторСлова, Контекст)
	
	СвойстваСлова = Контекст.СинтаксисЯзыка.СловаЯзыка.Получить(ВРег(ИдентификаторСлова));
	
	Если ВариантВстроенногоЯзыкаРусский() Тогда
		Слово = СвойстваСлова.ЯзыкРусский;
	Иначе
		Слово = СвойстваСлова.ЯзыкАнглийский;
	КонецЕсли;
	
	Если СвойстваСлова.ВерхнийРегистр Тогда
		Слово = ВРег(Слово);
	КонецЕсли;
	
	Возврат Слово;
	
КонецФункции

#КонецОбласти

#Область АнализИменТаблицИПолейТаблиц

// Проверка таблиц, полей таблиц и типов полей, найденных при разборе текста ограничения.
// Аналогичная процедура реализуется в СППР.
//
// Параметры:
//  РазобранноеОграничение - см. РазобранноеОграничение
//
Процедура ПроверитьТаблицыПоляИТипыПолей(РазобранноеОграничение)
	
	Контекст = Новый Структура;
	Контекст.Вставить("ТипыТаблиц", УправлениеДоступомСлужебныйПовтИсп.СинтаксисЯзыка().ТипыТаблиц);
	
	Для Каждого ТипТаблиц Из РазобранноеОграничение.ПоляТаблиц Цикл
		КоллекцияТаблиц = Метаданные[ТипТаблиц.Ключ]; // КоллекцияОбъектовМетаданных
		
		Для Каждого ПоляТаблицы Из ТипТаблиц.Значение Цикл
			
			МетаданныеТаблицы = КоллекцияТаблиц.Найти(ПоляТаблицы.Ключ);
			Если МетаданныеТаблицы = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ПоляТаблицы.Значение.ТаблицаСуществует = Истина;
			
			Контекст.Вставить("МетаданныеТаблицы",  МетаданныеТаблицы);
			Контекст.Вставить("СвойстваТипаТаблиц", Контекст.ТипыТаблиц.ПоКоллекциям.Получить(ТипТаблиц.Ключ));
			Контекст.Вставить("ЭтоОсновнаяТаблица", ПоляТаблицы.Значение.ЭтоОсновнаяТаблица);
			
			Для Каждого ПолеТаблицы Из ПоляТаблицы.Значение.Поля Цикл
				ОписаниеПоля = Новый Структура;
				ОписаниеПоля.Вставить("СоставИмени", СтрРазделить(ПолеТаблицы.Ключ, "."));
				ОписаниеПоля.Вставить("Свойства",    ПолеТаблицы.Значение);
				ОписаниеПоля.Вставить("ТипПоля",     Новый ОписаниеТипов);
				ПроверитьПолеТаблицы(ОписаниеПоля, Контекст);
				Если ОписаниеПоля.Свойства.ПолеСОшибкой = 0 Тогда
					ПроверитьТипыПоля(ОписаниеПоля, Контекст);
				КонецЕсли;
			КонецЦикла;
			
			ПроверитьРасширенияТаблицы(ПоляТаблицы, Контекст);
			ПроверитьПредопределенныеЗначенияТаблицы(ПоляТаблицы, Контекст);
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ПроверитьТаблицыПоляИТипыПолей, ПроверитьСледующееПолеЧерезТочку.
Процедура ПроверитьПолеТаблицы(ОписаниеПоля, Контекст, Индекс = 0, ПервыйВызов = Истина)
	
	СоставИмени  = ОписаниеПоля.СоставИмени;
	СвойстваПоля = ОписаниеПоля.Свойства;
	
	УточнениеПоля = Контекст.СвойстваТипаТаблиц.УточнениеПолей.Получить(ВРег(СоставИмени[Индекс]));
	
	Если УточнениеПоля <> Неопределено
	   И УточнениеПоля.Использование <> "Разрешено" Тогда
		
		СвойстваПоля.ПолеСОшибкой = Индекс + 1;
		СвойстваПоля.ВидОшибки = УточнениеПоля;
		Возврат;
	КонецЕсли;
	
	Свойства = СвойстваПоляИлиТабличнойЧасти(СоставИмени[Индекс], Контекст, Индекс = 0);
	Если Свойства = Неопределено Тогда
		СвойстваПоля.ПолеСОшибкой = Индекс + 1;
		СвойстваПоля.ВидОшибки = "НеНайдено";
		Возврат;
	КонецЕсли;
	Если Индекс = 0 Тогда
		СвойстваПоля.Коллекция = Свойства.Коллекция;
	КонецЕсли;
	
	Если Свойства.ЭтоТабличнаяЧасть Тогда
		Если Индекс > 0 Тогда
			СвойстваПоля.ПолеСОшибкой = Индекс + 1;
			СвойстваПоля.ВидОшибки = "ТабличнаяЧастьПослеТочки";
			Возврат;
		КонецЕсли;
		Если Не Контекст.ЭтоОсновнаяТаблица Тогда
			СвойстваПоля.ПолеСОшибкой = Индекс + 1;
			СвойстваПоля.ВидОшибки = "ТабличнаяЧастьДополнительнойТаблицы";
			Возврат;
		КонецЕсли;
		Если Индекс + 1 = СоставИмени.Количество() Тогда
			СвойстваПоля.ПолеСОшибкой = Индекс + 1;
			СвойстваПоля.ВидОшибки = "ТабличнаяЧастьБезПоля";
			Возврат;
		КонецЕсли;
		Индекс = Индекс + 1;
		Свойства = СвойстваПоляТабличнойЧасти(СоставИмени[Индекс],
			Свойства.Метаданные, Свойства.Коллекция, Контекст.МетаданныеТаблицы);
		Если Свойства = Неопределено Тогда
			СвойстваПоля.ПолеСОшибкой = Индекс + 1;
			СвойстваПоля.ВидОшибки = "НеНайдено";
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	ПроверитьСледующееПолеЧерезТочку(ОписаниеПоля, Индекс, Свойства, Контекст);
	
	Если ПервыйВызов Тогда
		ЗаполнитьТипыПоляСтрокойДополнительно(ОписаниеПоля);
	КонецЕсли;
	
КонецПроцедуры

// Для процедур ПроверитьПолеТаблицы и ПроверитьРасширенияТаблицы.
Процедура ПроверитьСледующееПолеЧерезТочку(ОписаниеПоля, Индекс, СвойстваТекущегоПоля, Контекст)
	
	ДобавитьТипыПоляДополнительно(ОписаниеПоля, Индекс, СвойстваТекущегоПоля, Контекст);
	
	Индекс = Индекс + 1;
	Если Индекс = ОписаниеПоля.СоставИмени.Количество() Тогда
		ОписаниеПоля.ТипПоля = Новый ОписаниеТипов(ОписаниеПоля.ТипПоля, СвойстваТекущегоПоля.Тип.Типы());
		Возврат;
	КонецЕсли;
	
	СвойстваПоля = ОписаниеПоля.Свойства;
	ПолеНайдено = Ложь;
	
	Если ОписаниеПоля.Свойство("ТаблицыСледующегоПоля") Тогда
		// См. процедуру ДобавитьТипыПоляДополнительно.
		Если ОписаниеПоля.ТаблицыСледующегоПоля.Количество() < Индекс + 1 Тогда
			ТаблицыСледующегоПоля = Новый Массив;
			ОписаниеПоля.ТаблицыСледующегоПоля.Добавить(ТаблицыСледующегоПоля);
		Иначе
			ТаблицыСледующегоПоля = ОписаниеПоля.ТаблицыСледующегоПоля[Индекс];
		КонецЕсли;
	КонецЕсли;
	
	Для Каждого Тип Из СвойстваТекущегоПоля.Тип.Типы() Цикл
		Если Не ОбщегоНазначения.ЭтоСсылка(Тип) Тогда
			Продолжить;
		КонецЕсли;
		СвойстваПоля.ПолеСОшибкой = 0;
		СвойстваПоля.ВидОшибки = "";
		
		// Сохранение текущего контекста.
		ТекущиеМетаданныеТаблицы  = Контекст.МетаданныеТаблицы;
		ТекущиеСвойстваТипаТаблиц = Контекст.СвойстваТипаТаблиц;
		
		Контекст.МетаданныеТаблицы = Метаданные.НайтиПоТипу(Тип);
		ПолноеИмя = Контекст.МетаданныеТаблицы.ПолноеИмя();
		СоставПолногоИмени = СтрРазделить(ПолноеИмя, ".", Ложь);
		Контекст.СвойстваТипаТаблиц = Контекст.ТипыТаблиц.ПоИменам.Получить(ВРег(СоставПолногоИмени[0]));
		
		ТекущийИндекс = Индекс;
		ПроверитьПолеТаблицы(ОписаниеПоля, Контекст, ТекущийИндекс, Ложь);
		
		// Восстановление текущего контекста.
		Контекст.МетаданныеТаблицы  = ТекущиеМетаданныеТаблицы;
		Контекст.СвойстваТипаТаблиц = ТекущиеСвойстваТипаТаблиц;
		
		Если СвойстваПоля.ПолеСОшибкой = 0 Тогда
			ПолеНайдено = Истина;
			Если ТаблицыСледующегоПоля <> Неопределено Тогда
				// См. процедуру ДобавитьТипыПоляДополнительно.
				ТаблицыСледующегоПоля.Добавить(ПолноеИмя);
			КонецЕсли;
		ИначеЕсли СвойстваПоля.ВидОшибки <> "НеНайдено" Тогда
			Возврат;
		КонецЕсли;
	КонецЦикла;
	
	Если ПолеНайдено Тогда
		СвойстваПоля.ПолеСОшибкой = 0;
		СвойстваПоля.ВидОшибки = "";
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ПроверитьПолеТаблицы.
Функция СвойстваПоляИлиТабличнойЧасти(ИмяПоляИлиТабличнойЧасти, Контекст, ЭтоПервоеПоле)
	
	Результат = Новый Структура;
	Результат.Вставить("ЭтоТабличнаяЧасть", Ложь);
	Результат.Вставить("Коллекция");
	Результат.Вставить("Метаданные");
	Результат.Вставить("Тип");
	
	МетаданныеТаблицы  = Контекст.МетаданныеТаблицы;
	СвойстваТипаТаблиц = Контекст.СвойстваТипаТаблиц;
	
	Для Каждого КоллекцияТабличныхЧастей Из СвойстваТипаТаблиц.КоллекцииТабличныхЧастей Цикл
		Если КоллекцияТабличныхЧастей.Ключ = "СтандартныеТабличныеЧасти" Тогда
			СтандартныеТабличныеЧасти = МетаданныеТаблицы.СтандартныеТабличныеЧасти; // ОписанияСтандартныхТабличныхЧастей
			Для Каждого СтандартнаяТабличнаяЧасть Из СтандартныеТабличныеЧасти Цикл
				Если ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег(СтандартнаяТабличнаяЧасть.Имя) Тогда
					Результат.Метаданные = СтандартнаяТабличнаяЧасть;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		Иначе
			Результат.Метаданные = МетаданныеТаблицы[КоллекцияТабличныхЧастей.Ключ].Найти(ИмяПоляИлиТабличнойЧасти);
		КонецЕсли;
		Если Результат.Метаданные <> Неопределено Тогда
			Результат.ЭтоТабличнаяЧасть = Истина;
			Результат.Коллекция = КоллекцияТабличныхЧастей.Ключ;
			Возврат Результат;
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого КоллекцияПолей Из СвойстваТипаТаблиц.КоллекцииПолей Цикл
		Если КоллекцияПолей.Ключ = "СтандартныеРеквизиты" Тогда
			Номер = 0;
			СтандартныеРеквизиты = МетаданныеТаблицы.СтандартныеРеквизиты; // ОписанияСтандартныхРеквизитов
			Для Каждого СтандартныйРеквизит Из СтандартныеРеквизиты Цикл
				Номер = Номер + 1;
				Если ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег(СтандартныйРеквизит.Имя) Тогда
					Результат.Метаданные = СтандартныйРеквизит;
					Прервать;
				КонецЕсли;
			КонецЦикла;
		Иначе
			Результат.Метаданные = МетаданныеТаблицы[КоллекцияПолей.Ключ].Найти(ИмяПоляИлиТабличнойЧасти);
		КонецЕсли;
		Если Результат.Метаданные <> Неопределено Тогда
			Результат.Коллекция = КоллекцияПолей.Ключ;
			Если Результат.Коллекция = "Графы" Тогда
				Результат.Тип = Новый ОписаниеТипов;
				МетаданныеГрафыЖурнала = Результат.Метаданные; // ОбъектМетаданныхГрафа
				Для Каждого МетаданныеСсылки Из МетаданныеГрафыЖурнала.Ссылки Цикл
					Результат.Тип = Новый ОписаниеТипов(Результат.Тип, МетаданныеСсылки.Тип.Типы());
				КонецЦикла;
			Иначе
				Результат.Тип = Результат.Метаданные.Тип;
			КонецЕсли;
			Возврат Результат;
		КонецЕсли;
	КонецЦикла;
	
	Если СвойстваТипаТаблиц.ОбщиеРеквизиты <> "Отсутствуют" Тогда
		Результат.Метаданные = Метаданные.ОбщиеРеквизиты.Найти(ИмяПоляИлиТабличнойЧасти);
		Если Результат.Метаданные <> Неопределено Тогда
			Результат.Коллекция = "ОбщиеРеквизиты";
			Результат.Тип = Результат.Метаданные.Тип;
			Возврат Результат;
		КонецЕсли;
	КонецЕсли;
	
	Если СвойстваТипаТаблиц.ИмяКоллекции = "Константы" Тогда
		
		Если ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег("Значение")
		 Или ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег("Value") Тогда
			
			Результат.Коллекция = "СпециальныеПоля";
			Результат.Тип = МетаданныеТаблицы.Тип;
			Результат.Вставить("ОсновнойПорядок", "001"); // См. процедуру ДобавитьОсновнойПорядокПоля.
			Возврат Результат;
		КонецЕсли;
		
	ИначеЕсли СвойстваТипаТаблиц.ИмяКоллекции = "Последовательности" Тогда
		
		Если ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег("Период")
		 Или ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег("Period") Тогда
		
			Результат.Коллекция = "СпециальныеПоля";
			Результат.Тип = Новый ОписаниеТипов("Дата");
			Результат.Вставить("ОсновнойПорядок", "001"); // См. процедуру ДобавитьОсновнойПорядокПоля.
			Возврат Результат;
		КонецЕсли;
		
		Если ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег("Регистратор")
		 Или ВРег(ИмяПоляИлиТабличнойЧасти) = ВРег("Recorder") Тогда
			
			Результат.Коллекция = "СпециальныеПоля";
			Результат.Тип = Новый ОписаниеТипов;
			МетаданныеПоследовательности = МетаданныеТаблицы; // ОбъектМетаданныхПоследовательность
			Для Каждого ДокументМетаданные Из МетаданныеПоследовательности.Документы Цикл
				Результат.Тип = Новый ОписаниеТипов(Результат.Тип, "ДокументСсылка." + ДокументМетаданные.Имя);
			КонецЦикла;
			Результат.Вставить("ОсновнойПорядок", "002"); // См. процедуру ДобавитьОсновнойПорядокПоля.
			Возврат Результат;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Для процедур ПроверитьРасширенияТаблицы, ПроверитьПолеТаблицы.
Функция СвойстваПоляТабличнойЧасти(ИмяПоляТабличнойЧасти, МетаданныеТабличнойЧасти, ИмяКоллекции, МетаданныеТаблицы)
	
	Результат = Новый Структура;
	Результат.Вставить("Тип");
	Результат.Вставить("ИмяТабличнойЧасти", МетаданныеТабличнойЧасти.Имя);
	
	Если ВРег(ИмяПоляТабличнойЧасти) = ВРег("Ссылка")
	 Или ВРег(ИмяПоляТабличнойЧасти) = ВРег("Ref") Тогда
		
		Для Каждого СтандартныйРеквизит Из МетаданныеТаблицы.СтандартныеРеквизиты Цикл
			Если ВРег(ИмяПоляТабличнойЧасти) = ВРег(СтандартныйРеквизит.Имя) Тогда
				Результат.Тип = СтандартныйРеквизит.Тип;
				Возврат Результат;
			КонецЕсли;
		КонецЦикла;
	КонецЕсли;
	
	Для Каждого СтандартныйРеквизит Из МетаданныеТабличнойЧасти.СтандартныеРеквизиты Цикл
		Если ВРег(ИмяПоляТабличнойЧасти) = ВРег(СтандартныйРеквизит.Имя) Тогда
			Результат.Тип = СтандартныйРеквизит.Тип;
			Возврат Результат;
		КонецЕсли;
	КонецЦикла;
	
	Если ИмяКоллекции = "ТабличныеЧасти" Тогда
		МетаданныеПоля = МетаданныеТабличнойЧасти.Реквизиты.Найти(ИмяПоляТабличнойЧасти);
		Если МетаданныеПоля <> Неопределено Тогда
			Результат.Тип = МетаданныеПоля.Тип;
			Результат.Вставить("МетаданныеТабличнойЧасти", МетаданныеТабличнойЧасти);
			Возврат Результат;
		КонецЕсли;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Для процедур ПроверитьРасширенияТаблицы, ПроверитьПолеТаблицы.
Функция СвойстваПоляПерерасчета(ИмяПоляПерерасчета, МетаданныеПерерасчета, МетаданныеТаблицы)
	
	Результат = Новый Структура;
	Результат.Вставить("Тип");
	Результат.Вставить("Коллекция");
	Результат.Вставить("Метаданные");
	
	Если ВРег(ИмяПоляПерерасчета) = ВРег("ОбъектПерерасчета")
	 Или ВРег(ИмяПоляПерерасчета) = ВРег("RecalculationObject") Тогда
		
		ИмяПоля = "Регистратор";
		
	ИначеЕсли ВРег(ИмяПоляПерерасчета) = ВРег("ВидРасчета")
	      Или ВРег(ИмяПоляПерерасчета) = ВРег("CalculationType") Тогда
	
		ИмяПоля = "ВидРасчета";
	КонецЕсли;
	
	МетаданныеРегистра = МетаданныеТаблицы; // ОбъектМетаданныхРегистрРасчета
	Если ЗначениеЗаполнено(ИмяПоля) Тогда
		Для Каждого СтандартныйРеквизит Из МетаданныеРегистра.СтандартныеРеквизиты Цикл
			Если ВРег(ИмяПоля) = ВРег(СтандартныйРеквизит.Имя) Тогда
				Результат.Коллекция = "СтандартныеРеквизиты";
				Результат.Метаданные = СтандартныйРеквизит;
				Результат.Тип = СтандартныйРеквизит.Тип;
				Возврат Результат;
			КонецЕсли;
		КонецЦикла;
		Возврат Неопределено;
	КонецЕсли;
	
	МетаданныеПоля = МетаданныеПерерасчета.Измерения.Найти(ИмяПоляПерерасчета);
	Если МетаданныеПоля <> Неопределено Тогда
		Результат.Тип = МетаданныеПоля.ИзмерениеРегистра.Тип;
		Результат.Коллекция = "СпециальныеПоля";
		Номер = 200 + МетаданныеРегистра.Измерения.Индекс(МетаданныеПоля) + 1;
		Результат.Вставить("ОсновнойПорядок", Номер); // См. процедуру ДобавитьОсновнойПорядокПоля.
		Возврат Результат;
	КонецЕсли;
	
	Возврат Неопределено;
	
КонецФункции

// Для процедуры ПроверитьПолеТаблицы.
Процедура ДобавитьТипыПоляДополнительно(ОписаниеПоля, Индекс, СвойстваТекущегоПоля, Контекст)
	
	// Дополнительный сбор типов для использования в служебных процедурах.
	
	// Расширение свойств узла Поле для использования в служебных процедурах.
	Если Индекс = 0 Или Индекс = 1 И СвойстваТекущегоПоля.Свойство("ИмяТабличнойЧасти") Тогда
		Если ОписаниеПоля.СоставИмени.Количество() > 1 Тогда
			ОписаниеПоля.Вставить("ТаблицыСледующегоПоля", Новый Массив);
			Если Индекс = 1 Тогда
				ОписаниеПоля.ТаблицыСледующегоПоля.Добавить(Неопределено);
			КонецЕсли;
		КонецЕсли;
		Если Индекс = 0 И Не Контекст.СвойстваТипаТаблиц.ЭтоСсылочныйТип Тогда
			ДобавитьОсновнойПорядокПоля(СвойстваТекущегоПоля, Контекст);
		КонецЕсли;
	КонецЕсли;
	
	Для Каждого УзелПоле Из ОписаниеПоля.Свойства.УзлыПоле Цикл
		Если СвойстваТекущегоПоля.Свойство("ОсновнойПорядок")
		   И Не УзелПоле.Свойство("ОсновнойПорядок") Тогда
			УзелПоле.Вставить("ОсновнойПорядок", СвойстваТекущегоПоля.ОсновнойПорядок);
		КонецЕсли;
		Если ОписаниеПоля.Свойство("ТаблицыСледующегоПоля")
		   И Не УзелПоле.Свойство("ТаблицыСледующегоПоля") Тогда
			УзелПоле.Вставить("ТаблицыСледующегоПоля", ОписаниеПоля.ТаблицыСледующегоПоля);
		КонецЕсли;
		Если Не УзелПоле.Свойство("ТипыПоля") Тогда
			УзелПоле.Вставить("ТипыПоля", Новый Массив);
			УстановитьПолеСодержитNull(УзелПоле, СвойстваТекущегоПоля, Контекст);
		КонецЕсли;
		Если СвойстваТекущегоПоля.Свойство("ИмяТабличнойЧасти")
		   И ВРег(СвойстваТекущегоПоля.ИмяТабличнойЧасти) = ВРег(ОписаниеПоля.СоставИмени[0]) Тогда
			
			УзелПоле.ТипыПоля.Добавить(СвойстваТекущегоПоля.ИмяТабличнойЧасти);
		КонецЕсли;
		Если Индекс > УзелПоле.ТипыПоля.Количество() - 1 Тогда
			УзелПоле.ТипыПоля.Добавить(СвойстваТекущегоПоля.Тип);
		КонецЕсли;
		УзелПоле.ТипыПоля[Индекс] = Новый ОписаниеТипов(УзелПоле.ТипыПоля[Индекс],
			СвойстваТекущегоПоля.Тип.Типы());
	КонецЦикла;
	
	// Добавление типов поля строкой для проверки изменения в служебных процедурах.
	ПолноеИмяПоля = Контекст.СвойстваТипаТаблиц.ЯзыкРусский + "." + ВРег(Контекст.МетаданныеТаблицы.Имя);
	
	Если СвойстваТекущегоПоля.Свойство("ИмяТабличнойЧасти") Тогда
		ПолноеИмяПоля = ПолноеИмяПоля + "." + ВРег(СвойстваТекущегоПоля.ИмяТабличнойЧасти);
	КонецЕсли;
	
	ПолноеИмяПоля = ПолноеИмяПоля + "." + ОписаниеПоля.СоставИмени[Индекс];
	
	Если Не ОписаниеПоля.Свойство("ВсеПоля") Тогда
		ОписаниеПоля.Вставить("ВсеПоля",       Новый Соответствие);
		ОписаниеПоля.Вставить("ТипыВсехПолей", Новый СписокЗначений);
	КонецЕсли;
	Если ОписаниеПоля.ВсеПоля.Получить(ПолноеИмяПоля) <> Неопределено Тогда
		Возврат;
	КонецЕсли;
	ОписаниеПоля.ВсеПоля.Вставить(ПолноеИмяПоля, Истина);
	
	ТипыСтрокой = СтрокаДанныхДляХеширования(СвойстваТекущегоПоля.Тип);
	ОписаниеПоля.ТипыВсехПолей.Добавить(ТипыСтрокой, ПолноеИмяПоля);
	
КонецПроцедуры

// Для процедуры ПроверитьПолеТаблицы.
Процедура ЗаполнитьТипыПоляСтрокойДополнительно(ОписаниеПоля)
	
	Если ОписаниеПоля.ТипыВсехПолей.Количество() > 1 Тогда
		ОписаниеПоля.ТипыВсехПолей.СортироватьПоПредставлению();
	КонецЕсли;
	ТипыСтрокой = СтрСоединить(ОписаниеПоля.ТипыВсехПолей.ВыгрузитьЗначения(), Символы.ПС);
	УзлыПоле = ОписаниеПоля.Свойства.УзлыПоле;
	
	Для Каждого УзелПоле Из УзлыПоле Цикл
		УзелПоле.Вставить("ТипыСтрокой", ТипыСтрокой);
	КонецЦикла;
	
	ОписаниеПоля.Удалить("ВсеПоля");
	ОписаниеПоля.Удалить("ТипыВсехПолей");
	
КонецПроцедуры

// Для процедуры ДобавитьТипыПоляДополнительно.
Процедура ДобавитьОсновнойПорядокПоля(СвойстваТекущегоПоля, Контекст)
	
	Если СвойстваТекущегоПоля.Коллекция = "СпециальныеПоля" Тогда
		Возврат; // Установлен в функции СвойстваПоляИлиТабличнойЧасти.
	КонецЕсли;
	
	МетаданныеТаблицы = Контекст.МетаданныеТаблицы; // ОбъектМетаданныхРегистрСведений
	МетаданныеПоля    = СвойстваТекущегоПоля.Метаданные;
	
	Если СвойстваТекущегоПоля.Коллекция = "СтандартныеРеквизиты" Тогда
		Индекс = 0;
		Для Каждого СтандартныйРеквизит Из МетаданныеТаблицы.СтандартныеРеквизиты Цикл
			Если СтандартныйРеквизит = МетаданныеПоля Тогда
				Прервать;
			КонецЕсли;
			Индекс = Индекс + 1;
		КонецЦикла;
		Номер = 100 + Индекс + 1;
		
	ИначеЕсли СвойстваТекущегоПоля.Коллекция = "Измерения" Тогда
		Номер = 200 + МетаданныеТаблицы.Измерения.Индекс(МетаданныеПоля) + 1;
		
	ИначеЕсли СвойстваТекущегоПоля.Коллекция = "Ресурсы" Тогда
		Номер = 300 + МетаданныеТаблицы.Ресурсы.Индекс(МетаданныеПоля) + 1;
		
	ИначеЕсли СвойстваТекущегоПоля.Коллекция = "Реквизиты" Тогда
		Номер = 400 + МетаданныеТаблицы.Реквизиты.Индекс(МетаданныеПоля) + 1;
		
	ИначеЕсли СвойстваТекущегоПоля.Коллекция = "ОбщиеРеквизиты" Тогда
		Номер = 500 + Метаданные.ОбщиеРеквизиты.Индекс(МетаданныеПоля) + 1;
	КонецЕсли;
	
	СвойстваТекущегоПоля.Вставить("ОсновнойПорядок", Строка(Номер));
	
КонецПроцедуры

// Для процедуры ДобавитьТипыПоляДополнительно.
Процедура УстановитьПолеСодержитNull(УзелПоле, СвойстваТекущегоПоля, Контекст)
	
	Если Контекст.СвойстваТипаТаблиц.ИмяКоллекции <> "Справочники"
	   И Контекст.СвойстваТипаТаблиц.ИмяКоллекции <> "ПланыВидовХарактеристик"
	 Или Не Контекст.МетаданныеТаблицы.Иерархический Тогда
		
		Возврат;
	КонецЕсли;
	
	Если Контекст.СвойстваТипаТаблиц.ИмяКоллекции = "Справочники"
	   И Контекст.МетаданныеТаблицы.ВидИерархии
	       <> Метаданные.СвойстваОбъектов.ВидИерархии.ИерархияГруппИЭлементов Тогда
		
		Возврат;
	КонецЕсли;
	
	Если СвойстваТекущегоПоля.Свойство("ИмяТабличнойЧасти") Тогда
		Если СвойстваТекущегоПоля.Свойство("МетаданныеТабличнойЧасти")
		   И СвойстваТекущегоПоля.МетаданныеТабличнойЧасти.Использование
		      <> Метаданные.СвойстваОбъектов.ИспользованиеРеквизита.ДляГруппыИЭлемента Тогда
			
			УзелПоле.Вставить("ПолеСодержитNull");
		КонецЕсли;
		
		Возврат;
	КонецЕсли;
	
	Если СвойстваТекущегоПоля.Коллекция = "Реквизиты"
	   И СвойстваТекущегоПоля.Метаданные.Использование
	      <> Метаданные.СвойстваОбъектов.ИспользованиеРеквизита.ДляГруппыИЭлемента Тогда
		
		УзелПоле.Вставить("ПолеСодержитNull");
	КонецЕсли;
	
КонецПроцедуры

// Для процедуры ПроверитьТаблицыПоляИТипыПолей.
Процедура ПроверитьРасширенияТаблицы(ПоляТаблицы, Контекст)
	
	СвойстваТипаТаблиц = Контекст.СвойстваТипаТаблиц;
	
	Если СвойстваТипаТаблиц.КоллекцииТабличныхЧастей.Количество() = 0
	   И СвойстваТипаТаблиц.ИмяКоллекции <> "РегистрыРасчета" Тогда
		Возврат;
	КонецЕсли;
	
	МетаданныеТаблицы = Контекст.МетаданныеТаблицы;
	
	Для Каждого РасширениеТаблицы Из ПоляТаблицы.Значение.Расширения Цикл
		
		Если СвойстваТипаТаблиц.ИмяКоллекции = "РегистрыРасчета" Тогда
			МетаданныеРасширения = МетаданныеТаблицы.Перерасчеты.Найти(РасширениеТаблицы.Ключ);
		Иначе
			Для Каждого КоллекцияТабличныхЧастей Из СвойстваТипаТаблиц.КоллекцииТабличныхЧастей Цикл
				Если КоллекцияТабличныхЧастей.Ключ = "СтандартныеТабличныеЧасти" Тогда
					СтандартныеТабличныеЧасти = МетаданныеТаблицы.СтандартныеТабличныеЧасти; // ОписанияСтандартныхТабличныхЧастей
					Для Каждого СтандартнаяТабличнаяЧасть Из СтандартныеТабличныеЧасти Цикл
						Если ВРег(РасширениеТаблицы.Ключ) = ВРег(СтандартнаяТабличнаяЧасть.Имя) Тогда
							МетаданныеРасширения = СтандартнаяТабличнаяЧасть;
							Прервать;
						КонецЕсли;
					КонецЦикла;
				Иначе
					МетаданныеРасширения = МетаданныеТаблицы[КоллекцияТабличныхЧастей.Ключ].Найти(РасширениеТаблицы.Ключ);
				КонецЕсли;
				Если МетаданныеРасширения <> Неопределено Тогда
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Если МетаданныеРасширения = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		РасширениеТаблицы.Значение.ТаблицаСуществует = Истина;
		
		Для Каждого ПолеТаблицы Из РасширениеТаблицы.Значение.Поля Цикл
			ОписаниеПоля = Новый Структура;
			ОписаниеПоля.Вставить("СоставИмени", СтрРазделить(ПолеТаблицы.Ключ, "."));
			ОписаниеПоля.Вставить("Свойства",    ПолеТаблицы.Значение);
			ОписаниеПоля.Вставить("ТипПоля",     Новый ОписаниеТипов);
			
			Индекс = 0;
			Если СвойстваТипаТаблиц.ИмяКоллекции = "РегистрыРасчета" Тогда
				СвойстваПоля = СвойстваПоляПерерасчета(ОписаниеПоля.СоставИмени[Индекс],
					МетаданныеРасширения, МетаданныеТаблицы);
			Иначе
				СвойстваПоля = СвойстваПоляТабличнойЧасти(ОписаниеПоля.СоставИмени[Индекс],
					МетаданныеРасширения, КоллекцияТабличныхЧастей.Ключ, МетаданныеТаблицы);
			КонецЕсли;
			
			Если СвойстваПоля = Неопределено Тогда
				ОписаниеПоля.Свойства.ПолеСОшибкой = Индекс + 1;
				ОписаниеПоля.Свойства.ВидОшибки = "НеНайдено";
				Продолжить;
			КонецЕсли;
			ПроверитьСледующееПолеЧерезТочку(ОписаниеПоля, Индекс, СвойстваПоля, Контекст);
			ЗаполнитьТипыПоляСтрокойДополнительно(ОписаниеПоля);
			Если ОписаниеПоля.Свойства.ПолеСОшибкой = 0 Тогда
				ПроверитьТипыПоля(ОписаниеПоля, Контекст);
			КонецЕсли;
		КонецЦикла;
		
	КонецЦикла;
	
КонецПроцедуры

// Для процедур ПроверитьТаблицыПоляИТипыПолей и ПроверитьРасширенияТаблицы.
Процедура ПроверитьТипыПоля(ОписаниеПоля, Контекст)
	
	ОписаниеТиповПоля = ОписаниеПоля.ТипПоля;
	
	Для Каждого ОписаниеТипа Из ОписаниеПоля.Свойства.СодержитТипы Цикл
		СвойстваТипа = ОписаниеТипа.Значение;
		Если ЗначениеЗаполнено(СвойстваТипа.ИмяКоллекцииТипа)
		   И Метаданные[СвойстваТипа.ИмяКоллекцииТипа].Найти(СвойстваТипа.ИмяОбъектаКоллекцииТипа) = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		Тип = Тип(ОписаниеТипа.Значение.ИмяТипа);
		СвойстваТипа.СодержитТип = ОписаниеТиповПоля.СодержитТип(Тип);
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ПроверитьТаблицыПоляИТипыПолей.
//
// Параметры:
//    ПоляТаблицы - см. НовыйСоставКоллекции
//
Процедура ПроверитьПредопределенныеЗначенияТаблицы(ПоляТаблицы, Контекст)
	
	СвойстваТипаТаблиц = Контекст.СвойстваТипаТаблиц;
	
	СвойстваТаблицы = ПоляТаблицы.Значение; // см. НовыеСвойстваТаблицы
	Если СвойстваТаблицы.Предопределенные.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	Если СвойстваТипаТаблиц.ЕстьПредопределенные Тогда
		МетаданныеТаблицы = Контекст.МетаданныеТаблицы;
		
		Если СвойстваТипаТаблиц.ИмяКоллекции = "Перечисления" Тогда
			ИменаПредопределенных = Новый Массив;
			Для Каждого ЗначениеПеречисления Из МетаданныеТаблицы.ЗначенияПеречисления Цикл
				ТекущееЗначениеПеречисления = ЗначениеПеречисления; // ОбъектМетаданныхЗначениеПеречисления
				ИменаПредопределенных.Добавить(ТекущееЗначениеПеречисления.Имя);
			КонецЦикла;
		Иначе
			ИменаПредопределенных = Новый Массив(МетаданныеТаблицы.ПолучитьИменаПредопределенных());
		КонецЕсли;
	Иначе
		ИменаПредопределенных = Новый Массив;
	КонецЕсли;
	
	ИменаПредопределенных.Добавить("ПустаяСсылка");
	ИменаПредопределенных.Добавить("EmptyRef");
	
	Для Каждого Предопределенный Из СвойстваТаблицы.Предопределенные Цикл
		Для Каждого ИмяПредопределенного Из ИменаПредопределенных Цикл
			Если ВРег(ИмяПредопределенного) = Предопределенный.Ключ Тогда
				Предопределенный.Значение.ИмяСуществует = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры


// Для функции СтруктураОграничения.
Процедура ОтметитьНекорректныеИменаТаблицПолейИТиповПолей(ПоляТаблиц, Контекст)
	
	Для Каждого ТипТаблиц Из ПоляТаблиц Цикл
		Для Каждого ОписаниеТаблицы Из ТипТаблиц.Значение Цикл
			СвойстваТаблицы = ОписаниеТаблицы.Значение; // см. НовыеСвойстваТаблицы
			
			Если Не СвойстваТаблицы.ТаблицаСуществует Тогда
				Для Каждого Источник Из СвойстваТаблицы.Источники Цикл
					УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'Не существует таблица ""%1""'"), Источник.Символы), , 2);
				КонецЦикла;
				Если СвойстваТаблицы.Свойство("ПервоеПоле")
				   И СвойстваТаблицы.ПервоеПоле.ПервыйИсточник <> Неопределено Тогда
					
					УстановитьОшибкуВСтрокеИмениПоля(Контекст, СвойстваТаблицы.ПервоеПоле.ПервыйИсточник.Ключ,
						НСтр("ru = 'Поле не существует, так как указана несуществующая таблица ""%1""'"), 0, ,
						СвойстваТаблицы.ПервоеПоле.ПервыйИсточник.Значение);
				КонецЕсли;
				Для Каждого ОписаниеПредопределенного Из СвойстваТаблицы.Предопределенные Цикл
					Для Каждого Источник Из ОписаниеПредопределенного.Значение.Источники Цикл
						СоставИмени = СтрРазделить(Источник.Символы, ".");
						
						УстановитьОшибкуВСтроке(Источник, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
								НСтр("ru = 'Предопределенное значение не существует, так как указана несуществующая таблица ""%1""'"),
								СоставИмени[0] + "." + СоставИмени[1]), , 2);
					КонецЦикла;
				КонецЦикла;
				Продолжить;
			КонецЕсли;
			
			Для Каждого ОписаниеПредопределенного Из СвойстваТаблицы.Предопределенные Цикл
				СвойстваПредопределенного = ОписаниеПредопределенного.Значение;
				Если СвойстваПредопределенного.ИмяСуществует Тогда
					Продолжить;
				КонецЕсли;
				Для Каждого Источник Из СвойстваПредопределенного.Источники Цикл
					СоставИмени = СтрРазделить(Источник.Символы, ".");
					Источник.ПозицияОшибки = СтрДлина(СоставИмени[0] + "." + СоставИмени[1]) + 1;
					Источник.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
						НСтр("ru = 'Не существует предопределенное значение ""%1""'"), СоставИмени[2]);
				КонецЦикла;
			КонецЦикла;
			
			Для Каждого ОписаниеПоля Из СвойстваТаблицы.Поля Цикл
				ОтметитьНекорректноеПолеИТипыПоля(ОписаниеПоля, Контекст);
			КонецЦикла;
			
			Для Каждого ОписаниеРасширения Из СвойстваТаблицы.Расширения Цикл
				СвойстваРасширения = ОписаниеРасширения.Значение;
				Если Не СвойстваРасширения.ТаблицаСуществует Тогда
					Для Каждого Источник Из СвойстваРасширения.Источники Цикл
						УстановитьОшибкуВСтроке(Источник,
							СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
								НСтр("ru = 'Не существует таблица ""%1""'"), Источник.Символы), , 2);
					КонецЦикла;
					Если СвойстваРасширения.Свойство("ПервоеПоле")
					   И СвойстваРасширения.ПервоеПоле.ПервыйИсточник <> Неопределено Тогда
						
						УстановитьОшибкуВСтрокеИмениПоля(Контекст, СвойстваРасширения.ПервоеПоле.ПервыйИсточник.Ключ,
							НСтр("ru = 'Поле не существует, так как указана несуществующая таблица ""%1""'"), 0, ,
							СвойстваРасширения.ПервоеПоле.ПервыйИсточник.Значение);
					КонецЕсли;
					Продолжить;
				КонецЕсли;
				Для Каждого ОписаниеПоля Из СвойстваРасширения.Поля Цикл
					ОтметитьНекорректноеПолеИТипыПоля(ОписаниеПоля, Контекст);
				КонецЦикла;
			КонецЦикла;
			
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОтметитьНекорректныеИменаТаблицПолейИТиповПолей.
Процедура ОтметитьНекорректноеПолеИТипыПоля(ОписаниеПоля, Контекст)
	
	СвойстваПоля = ОписаниеПоля.Значение;
	Если СвойстваПоля.ПолеСОшибкой = 1 Тогда
		Для Каждого ОписаниеИсточника Из ОписаниеПоля.Значение.Источники Цикл
			ВКонце = Ложь;
			Если СвойстваПоля.ВидОшибки = "ТабличнаяЧастьБезПоля" Тогда
				ВКонце = Истина;
				ШаблонОшибки = НСтр("ru = 'Не указано поле после табличной части ""%1"" таблицы ""%2""'");
				
			ИначеЕсли СвойстваПоля.ВидОшибки = "ТабличнаяЧастьДополнительнойТаблицы" Тогда
				ШаблонОшибки = НСтр("ru = 'Табличная часть ""%1"" не поддерживается для дополнительной таблицы ""%2""'");
				
			ИначеЕсли СвойстваПоля.ВидОшибки = "Недопустимо" Тогда
				ШаблонОшибки = НСтр("ru = 'Недопустимо использовать поле ""%1"" таблицы ""%2""'");
				
			ИначеЕсли СвойстваПоля.ВидОшибки = "Запрещено" Тогда
				ШаблонОшибки = НСтр("ru = 'Запрещено использовать поле ""%1"" таблицы ""%2""'");
			Иначе
				ШаблонОшибки = НСтр("ru = 'Не существует поле ""%1"" таблицы ""%2""'");
			КонецЕсли;
			УстановитьОшибкуВСтрокеИмениПоля(Контекст,
				 ОписаниеИсточника.Ключ, ШаблонОшибки, 1, Истина, ОписаниеИсточника.Значение, ВКонце);
		КонецЦикла;
		Возврат;
	КонецЕсли;
	
	Если СвойстваПоля.ПолеСОшибкой > 1 Тогда
		Для Каждого ОписаниеИсточника Из ОписаниеПоля.Значение.Источники Цикл
			Если СвойстваПоля.ВидОшибки = "ТабличнаяЧастьПослеТочки" Тогда
				ШаблонОшибки = НСтр("ru = 'Табличная часть ""%1"" не поддерживается ""через точку"" от поля'");
				
			ИначеЕсли СвойстваПоля.ВидОшибки = "Недопустимо" Тогда
				ШаблонОшибки = НСтр("ru = 'Недопустимо использовать поле ""%1""'");
				
			ИначеЕсли СвойстваПоля.ВидОшибки = "Запрещено" Тогда
				ШаблонОшибки = НСтр("ru = 'Запрещено использовать поле ""%1""'");
			Иначе
				ШаблонОшибки = НСтр("ru = 'Не существует поле ""%1""'");
			КонецЕсли;
			УстановитьОшибкуВСтрокеИмениПоля(Контекст,
				ОписаниеИсточника.Ключ, ШаблонОшибки, СвойстваПоля.ПолеСОшибкой, Истина);
		КонецЦикла;
		Возврат;
	КонецЕсли;
	
	СоставИмени = СтрРазделить(ОписаниеПоля.Ключ, ".");
	Если СоставИмени.Количество() > 1
	   И (    СоставИмени[1] = ВРег("Ссылка")
	      Или СоставИмени[1] = ВРег("Ref") )
	   И СвойстваПоля.Коллекция <> "ТабличныеЧасти"
	   И СвойстваПоля.Коллекция <> "СтандартныеТабличныеЧасти" Тогда
		
			Для Каждого ОписаниеИсточника Из ОписаниеПоля.Значение.Источники Цикл
				ШаблонОшибки = НСтр("ru = 'Поле ""%1"" избыточно указывать ""через точку"" от любого поля'");
				УстановитьОшибкуВСтрокеИмениПоля(Контекст,
					ОписаниеИсточника.Ключ, ШаблонОшибки, 2, Истина);
		КонецЦикла;
		Возврат;
	КонецЕсли;
	
	Для Каждого ОписаниеТипа Из СвойстваПоля.СодержитТипы Цикл
		СвойстваТипа = ОписаниеТипа.Значение;
		Для Каждого ОписаниеИсточника Из ОписаниеТипа.Значение.Источники Цикл
			Если ТипЗнч(ОписаниеИсточника.Ключ) = Тип("СтрокаТаблицыЗначений") Тогда
				Если Не СвойстваТипа.СодержитТип Тогда
					УстановитьОшибкуВСтроке(ОписаниеИсточника.Ключ,
						СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
							НСтр("ru = 'У поля ""%1"" не существует тип ""%2""'"),
							ОписаниеИсточника.Значение.Символы,
							ОписаниеИсточника.Ключ.Символы),
						, 2);
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
КонецПроцедуры

// Для процедуры ОтметитьНекорректныеИменаТаблицПолейИТиповПолей.
Процедура УстановитьОшибкуВСтрокеИмениПоля(Контекст, Строка, ШаблонОшибки, ПолеСОшибкой,
			ВставитьИмя = Ложь, Таблица = Null, ВКонце = Ложь)
	
	Если ЗначениеЗаполнено(Строка.ТекстОшибки) Тогда
		Возврат;
	КонецЕсли;
	
	Если ТипЗнч(ВставитьИмя) = Тип("Строка") Тогда
		СоставИмени = СтрРазделить(ВставитьИмя, ".");
		ВставитьИмя = Истина;
	Иначе
		СоставИмени = СтрРазделить(Строка.Символы, ".");
	КонецЕсли;
	
	Если СоставИмени.Количество() > 1
	   И Контекст.Псевдонимы.Получить(ВРег(СоставИмени[0])) <> Неопределено Тогда
		
		Строка.ПозицияОшибки = СтрДлина(СоставИмени[0]) + 1;
		СоставИмени.Удалить(0);
	КонецЕсли;
	
	Для Номер = 1 По ПолеСОшибкой - 1 Цикл
		Строка.ПозицияОшибки = Строка.ПозицияОшибки + СтрДлина(СоставИмени[0]) + 1;
		СоставИмени.Удалить(0);
	КонецЦикла;
	Если ВКонце Тогда
		Строка.ПозицияОшибки = Строка.ПозицияОшибки + СтрДлина(СоставИмени[0]);
	КонецЕсли;
	ИмяПоля = СоставИмени[0];
	
	Если ВставитьИмя И Таблица <> Null Тогда
		Строка.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
			ИмяПоля, ?(ЗначениеЗаполнено(Таблица), Таблица, Контекст.ОсновнаяТаблица));
			
	ИначеЕсли ВставитьИмя Тогда
		Строка.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки, ИмяПоля);
		
	ИначеЕсли Таблица <> Null Тогда
		Строка.ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонОшибки,
			?(ЗначениеЗаполнено(Таблица), Таблица, Контекст.ОсновнаяТаблица));
	Иначе
		Строка.ТекстОшибки = ШаблонОшибки;
	КонецЕсли;
	
КонецПроцедуры

#КонецОбласти

#КонецОбласти

#Область ОбновлениеПрогрессаОбновленияДоступа

// Возвращаемое значение:
//  Структура:
//   * СтрокиСписков - Соответствие
//   * СвойстваСписков - Соответствие
//   * КоличествоКлючей - Число
//   * ДатаПоследнегоОбновления - Дата
//
Функция НовыеХранимыеДанныеОбновленияПрогресса() Экспорт
	
	ХранимыеДанные = Новый Структура;
	ХранимыеДанные.Вставить("СтрокиСписков",    Новый Соответствие);
	ХранимыеДанные.Вставить("СвойстваСписков",  Новый Соответствие);
	ХранимыеДанные.Вставить("КоличествоКлючей", 0);
	ХранимыеДанные.Вставить("ДатаПоследнегоОбновления", '00010101');
	
	Возврат ХранимыеДанные;
	
КонецФункции

// Параметры:
//  Контекст - Структура:
//   * Версия - Число
//   * ХранимыеДанные - см. НовыеХранимыеДанныеОбновленияПрогресса
//   * РассчитыватьПоКоличествуДанных - Булево
//   * ПоказыватьОбработанныеСписки - Булево
//   * ЭтоПовторноеОбновлениеПрогресса - Булево
//   * ВсегоОбновлено - Число
//   * ПериодОбновленияПрогресса - Число
//   * АвтообновлениеПрогресса - Булево
//   * ДобавленныеСтроки - Массив
//   * УдаленныеСтроки - Соответствие
//   * ИзмененныеСтроки - Соответствие
//   * ОбновлениеДоступаВыполняется - Булево
//  
//  АдресРезультата - Строка
//
Процедура ОбновитьПрогрессВФоне(Контекст, АдресРезультата) Экспорт
	
	Если Контекст.Свойство("Версия") И Контекст.Версия = 1 Тогда
		Попытка
			ОбновитьПрогресс(Контекст);
		Исключение
			ИнформацияОбОшибке = ИнформацияОбОшибке();
			Если СтандартныеПодсистемыСервер.ЭтоОшибкаТребованияПерезапускаСеанса(ИнформацияОбОшибке) Тогда
				ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке);
			Иначе
				ТекстОшибки = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке);
			КонецЕсли;
			Контекст.Вставить("ТекстОшибки", ТекстОшибки);
		КонецПопытки;
	Иначе
		Контекст.Вставить("ТекстОшибки",
			НСтр("ru = 'Версия программы обновлена, перезапустите клиентский сеанс.'"));
	КонецЕсли;
	
	ПоместитьВоВременноеХранилище(Контекст, АдресРезультата);
	
КонецПроцедуры

Процедура ОбновитьПрогресс(Контекст)
	
	ДатаНачалаОбновленияПрогресса = ТекущаяДатаСеанса();
	ДлительныеОперации.СообщитьПрогресс(0); // Обновление прогресса выполняется.
	
	СтрокиСписков   = Контекст.ХранимыеДанные.СтрокиСписков;
	СвойстваСписков = Контекст.ХранимыеДанные.СвойстваСписков;
	Если Не Контекст.ЭтоПовторноеОбновлениеПрогресса Тогда
		Контекст.ХранимыеДанные.ДатаПоследнегоОбновления = '00010101';
	КонецЕсли;
	
	ДействующиеПараметры = Неопределено; // См. ДействующиеПараметрыОграниченияДоступа
	ИдентификаторыТаблиц = ИдентификаторыСписковСОграничением(ДействующиеПараметры);
	
	Если Контекст.ПоказыватьОбработанныеСписки Тогда
		Для Каждого КлючИЗначение Из ИдентификаторыТаблиц Цикл
			Если СтрокиСписков.Получить(КлючИЗначение.Значение) <> Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ДобавитьНовуюСтрокуСписка(Контекст, КлючИЗначение.Значение, КлючИЗначение.Ключ);
		КонецЦикла;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.УстановитьПараметр("МаксимальнаяДата", МаксимальнаяДатаПриПродолжении());
	Запрос.УстановитьПараметр("ДатаПоследнегоОбновления", Контекст.ХранимыеДанные.ДатаПоследнегоОбновления);
	Запрос.УстановитьПараметр("ПустойУникальныйИдентификатор",
		ОбщегоНазначенияКлиентСервер.ПустойУникальныйИдентификатор());
	
	Контекст.ХранимыеДанные.ДатаПоследнегоОбновления = ДатаНачалаОбновленияПрогресса;
	
	ТекстыЗапросов = Новый Массив;
	ТекстыЗапросов.Добавить(
	"ВЫБРАТЬ
	|	ВсеСпискиОбновления.Список КАК Список,
	|	МАКСИМУМ(ВсеСпискиОбновления.ОбновлениеЭлементов) КАК ОбновлениеЭлементов,
	|	МАКСИМУМ(ВсеСпискиОбновления.ОбновлениеКлючейДоступа) КАК ОбновлениеКлючейДоступа
	|ИЗ
	|	(ВЫБРАТЬ РАЗЛИЧНЫЕ
	|		ОбновлениеКлючейДоступаКДанным.Список КАК Список,
	|		ИСТИНА КАК ОбновлениеЭлементов,
	|		ЛОЖЬ КАК ОбновлениеКлючейДоступа
	|	ИЗ
	|		РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК ОбновлениеКлючейДоступаКДанным
	|	
	|	ОБЪЕДИНИТЬ ВСЕ
	|	
	|	ВЫБРАТЬ РАЗЛИЧНЫЕ
	|		ОбновлениеКлючейДоступаПользователей.Список,
	|		ЛОЖЬ,
	|		ИСТИНА
	|	ИЗ
	|		РегистрСведений.ОбновлениеКлючейДоступаПользователей КАК ОбновлениеКлючейДоступаПользователей) КАК ВсеСпискиОбновления
	|
	|СГРУППИРОВАТЬ ПО
	|	ВсеСпискиОбновления.Список");
	
	ТекстыЗапросов.Добавить(
	"ВЫБРАТЬ
	|	ОбновлениеКлючей.Список КАК Список,
	|	ОбновлениеКлючей.ДляВнешнихПользователей КАК ДляВнешнихПользователей,
	|	ОбновлениеКлючей.КлючУникальности = &ПустойУникальныйИдентификатор КАК ЭтоОсновнаяЗапись,
	|	МАКСИМУМ(ОбновлениеКлючей.РазмерЗадания) КАК РазмерЗадания,
	|	МАКСИМУМ(ОбновлениеКлючей.ДатаИзмененияЗаписиРегистра) КАК МаксимальнаяДатаИзменения,
	|	МИНИМУМ(ОбновлениеКлючей.ДатаИзмененияЗаписиРегистра) КАК МинимальнаяДатаИзменения
	|ИЗ
	|	РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК ОбновлениеКлючей
	|ГДЕ
	|	ОбновлениеКлючей.ДатаИзмененияЗаписиРегистра >= &ДатаПоследнегоОбновления
	|	И ОбновлениеКлючей.Список <> НЕОПРЕДЕЛЕНО
	|
	|СГРУППИРОВАТЬ ПО
	|	ОбновлениеКлючей.Список,
	|	ОбновлениеКлючей.ДляВнешнихПользователей,
	|	ОбновлениеКлючей.КлючУникальности = &ПустойУникальныйИдентификатор
	|ИТОГИ ПО
	|	Список,
	|	ДляВнешнихПользователей");
	
	ТекстыЗапросов.Добавить(СтрЗаменить(ТекстыЗапросов[1],
		"РегистрСведений.ОбновлениеКлючейДоступаКДанным",
		"РегистрСведений.ОбновлениеКлючейДоступаПользователей"));
	
	Если Контекст.РассчитыватьПоКоличествуДанных Тогда
		ТекстыЗапросов.Добавить(
		"ВЫБРАТЬ
		|	ОбновлениеКлючей.Список КАК Список,
		|	ОбновлениеКлючей.ДляВнешнихПользователей КАК ДляВнешнихПользователей,
		|	ОбновлениеКлючей.ПараметрыЗадания КАК ПараметрыЗадания
		|ИЗ
		|	РегистрСведений.ОбновлениеКлючейДоступаКДанным КАК ОбновлениеКлючей
		|ГДЕ
		|	ОбновлениеКлючей.ДатаИзмененияЗаписиРегистра >= &ДатаПоследнегоОбновления
		|	И ОбновлениеКлючей.КлючУникальности = &ПустойУникальныйИдентификатор
		|	И ОбновлениеКлючей.Список <> НЕОПРЕДЕЛЕНО
		|ИТОГИ ПО
		|	Список");
		
		ТекстыЗапросов.Добавить(СтрЗаменить(ТекстыЗапросов[3],
			"РегистрСведений.ОбновлениеКлючейДоступаКДанным",
			"РегистрСведений.ОбновлениеКлючейДоступаПользователей"));
		
		ТекстыЗапросов.Добавить(
		"ВЫБРАТЬ
		|	КОЛИЧЕСТВО(*) КАК Количество
		|ИЗ
		|	Справочник.КлючиДоступа КАК КлючиДоступа");
	КонецЕсли;
	
	Запрос.Текст = СтрСоединить(ТекстыЗапросов, ОбщегоНазначения.РазделительПакетаЗапросов());
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	
	ВсеСпискиОбновления = РезультатыЗапроса[0].Выгрузить();
	НовыеСписки = Новый Массив;
	
	Для Каждого СписокОбновления Из ВсеСпискиОбновления Цикл
		Если Не ЗначениеЗаполнено(СписокОбновления.Список) Тогда
			Продолжить;
		КонецЕсли;
		Строка = СтрокиСписков.Получить(СписокОбновления.Список);
		Если Строка = Неопределено Тогда
			НовыеСписки.Добавить(СписокОбновления.Список);
		КонецЕсли;
	КонецЦикла;
	
	Если НовыеСписки.Количество() > 0 Тогда
		ОбъектыМетаданныхПоИдентификаторам = ОбщегоНазначения.ОбъектыМетаданныхПоИдентификаторам(НовыеСписки, Ложь);
		Для Каждого НовыйСписок Из НовыеСписки Цикл
			ОбъектМетаданных = ОбъектыМетаданныхПоИдентификаторам.Получить(НовыйСписок);
			Если ОбъектМетаданных = Неопределено Тогда
				Продолжить;
			ИначеЕсли ТипЗнч(ОбъектМетаданных) = Тип("ОбъектМетаданных") Тогда
				ИмяТаблицы = ОбъектМетаданных.ПолноеИмя();
			Иначе
				ИмяТаблицы = "";
			КонецЕсли;
			ДобавитьНовуюСтрокуСписка(Контекст, НовыйСписок, ИмяТаблицы);
		КонецЦикла;
	КонецЕсли;
	
	СтрокиОбновленияКоличестваЭлементов = Новый Массив;
	СтрокиОбновленияКоличестваКлючейДоступа = Новый Массив;
	УдаляемыеСтроки = Новый Массив;
	Для Каждого КлючИЗначение Из СтрокиСписков Цикл
		Строка = КлючИЗначение.Значение;
		СписокОбновления = ВсеСпискиОбновления.Найти(Строка.Список, "Список");
		Если СписокОбновления = Неопределено И Не Контекст.ПоказыватьОбработанныеСписки Тогда
			УдаляемыеСтроки.Добавить(Строка);
			Продолжить;
		КонецЕсли;
		СвойстваСписка = СвойстваСписков.Получить(Строка.Список);
		Если СписокОбновления = Неопределено Или Не СписокОбновления.ОбновлениеЭлементов Тогда
			СвойстваСписка.РазмерЗаданияОбновленияЭлементов = 0;
			СвойстваСписка.ПоследнийОбновленныйЭлемент = Null;
			СвойстваСписка.РазмерЗаданияОбновленияЭлементовДляВнешнихПользователей = 0;
			СвойстваСписка.ПоследнийОбновленныйЭлементДляВнешнихПользователей = Null;
			Строка.ДоляОбработанныхЭлементовДляПользователей = 1;
			Строка.ДоляОбработанныхЭлементовДляВнешнихПользователей = 1;
			Если Контекст.РассчитыватьПоКоличествуДанных Тогда
				Если Строка.ОбработаноЭлементов <> 100 Тогда
					СтрокиОбновленияКоличестваЭлементов.Добавить(Строка);
				КонецЕсли;
			Иначе
				ОбнулитьКоличествоЭлементов(Строка, Контекст);
			КонецЕсли;
			ОбновитьЗначениеВСтроке(Строка.ОбработаноЭлементов, 100, Строка, Контекст);
		КонецЕсли;
		Если СписокОбновления = Неопределено Или Не СписокОбновления.ОбновлениеКлючейДоступа Тогда
			СвойстваСписка.РазмерЗаданияОбновленияКлючейДоступа = 0;
			СвойстваСписка.ПоследнийОбновленныйКлючДоступа = Null;
			СвойстваСписка.РазмерЗаданияОбновленияКлючейДоступаДляВнешнихПользователей = 0;
			СвойстваСписка.ПоследнийОбновленныйКлючДоступаДляВнешнихПользователей = Null;
			Строка.ДоляОбработанныхКлючейДоступаДляПользователей = 1;
			Строка.ДоляОбработанныхКлючейДоступаДляВнешнихПользователей = 1;
			Если Контекст.РассчитыватьПоКоличествуДанных Тогда
				Если Строка.ОбработаноКлючейДоступа <> 100 Тогда
					СтрокиОбновленияКоличестваКлючейДоступа.Добавить(Строка);
				КонецЕсли;
			Иначе
				ОбнулитьКоличествоКлючейДоступа(Строка, Контекст);
			КонецЕсли;
			ОбновитьЗначениеВСтроке(Строка.ОбработаноКлючейДоступа, 100, Строка, Контекст);
		КонецЕсли;
		Если СписокОбновления = Неопределено Тогда
			ОбновитьЗначениеВСтроке(Строка.ПоследнееОбновление, '00010101', Строка, Контекст);
			ОбновитьЗначениеВСтроке(Строка.ПервоеПланированиеОбновления, МаксимальнаяДата(), Строка, Контекст);
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого УдаляемаяСтрока Из УдаляемыеСтроки Цикл
		СтрокиСписков.Удалить(УдаляемаяСтрока.Список);
		СвойстваСписков.Удалить(УдаляемаяСтрока.Список);
		Индекс = Контекст.ДобавленныеСтроки.Найти(УдаляемаяСтрока);
		Если Индекс <> Неопределено Тогда
			Контекст.ДобавленныеСтроки.Удалить(Индекс);
			Продолжить;
		КонецЕсли;
		Контекст.УдаленныеСтроки.Вставить(УдаляемаяСтрока.Список, Истина);
	КонецЦикла;
	
	Если Контекст.РассчитыватьПоКоличествуДанных Тогда
		ВыборкаПоСпискам = РезультатыЗапроса[3].Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
		Пока ВыборкаПоСпискам.Следующий() Цикл
			СвойстваСписка = СвойстваСписков.Получить(ВыборкаПоСпискам.Список);
			Если СвойстваСписка = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ВыборкаПоВидамПользователей = ВыборкаПоСпискам.Выбрать();
			Пока ВыборкаПоВидамПользователей.Следующий() Цикл
				Если ВыборкаПоВидамПользователей.ДляВнешнихПользователей Тогда
					СвойстваСписка.ПоследнийОбновленныйЭлементДляВнешнихПользователей
						= ВыборкаПоВидамПользователей.ПараметрыЗадания;
				Иначе
					СвойстваСписка.ПоследнийОбновленныйЭлемент
						= ВыборкаПоВидамПользователей.ПараметрыЗадания;
				КонецЕсли;
			КонецЦикла;
		КонецЦикла;
		ВыборкаПоСпискам = РезультатыЗапроса[4].Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
		Пока ВыборкаПоСпискам.Следующий() Цикл
			СвойстваСписка = СвойстваСписков.Получить(ВыборкаПоСпискам.Список);
			Если СвойстваСписка = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			ВыборкаПоВидамПользователей = ВыборкаПоСпискам.Выбрать();
			Пока ВыборкаПоВидамПользователей.Следующий() Цикл
				Если ВыборкаПоВидамПользователей.ДляВнешнихПользователей Тогда
					СвойстваСписка.ПоследнийОбновленныйКлючДоступаДляВнешнихПользователей
						= ВыборкаПоВидамПользователей.ПараметрыЗадания;
				Иначе
					СвойстваСписка.ПоследнийОбновленныйКлючДоступа
						= ВыборкаПоВидамПользователей.ПараметрыЗадания;
				КонецЕсли;
			КонецЦикла;
		КонецЦикла;
	КонецЕсли;
	
	СпискиОбновленияЭлементов = РезультатыЗапроса[1].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	Для Каждого ОписаниеОбновления Из СпискиОбновленияЭлементов.Строки Цикл
		Строка = СтрокиСписков.Получить(ОписаниеОбновления.Список);
		Если Строка = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		СвойстваСписка = СвойстваСписков.Получить(Строка.Список);
		ЗаполнитьДолиОбработанных(Строка, ОписаниеОбновления, СвойстваСписка, Истина, Контекст);
		Если Не Контекст.РассчитыватьПоКоличествуДанных Тогда
			Обработано = ОбработаноПоДолям(Строка.ДоляОбработанныхЭлементовДляПользователей,
				Строка.ДоляОбработанныхЭлементовДляВнешнихПользователей, Строка.ИмяТаблицы, ДействующиеПараметры);
			ОбновитьЗначениеВСтроке(Строка.ОбработаноЭлементов, Обработано, Строка, Контекст);
			ОбнулитьКоличествоЭлементов(Строка, Контекст);
		Иначе
			Если Контекст.ЭтоПовторноеОбновлениеПрогресса Тогда
				СтрокиОбновленияКоличестваЭлементов.Добавить(Строка);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	СпискиОбновленияКлючейДоступа = РезультатыЗапроса[2].Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам);
	Для Каждого ОписаниеОбновления Из СпискиОбновленияКлючейДоступа.Строки Цикл
		Строка = СтрокиСписков.Получить(ОписаниеОбновления.Список);
		Если Строка = Неопределено Тогда
			Продолжить;
		КонецЕсли;
		СвойстваСписка = СвойстваСписков.Получить(Строка.Список);
		ЗаполнитьДолиОбработанных(Строка, ОписаниеОбновления, СвойстваСписка, Ложь, Контекст);
		Если Не Контекст.РассчитыватьПоКоличествуДанных Тогда
			Обработано = ОбработаноПоДолям(Строка.ДоляОбработанныхКлючейДоступаДляПользователей,
				Строка.ДоляОбработанныхКлючейДоступаДляВнешнихПользователей, Строка.ИмяТаблицы, ДействующиеПараметры);
			ОбновитьЗначениеВСтроке(Строка.ОбработаноКлючейДоступа, Обработано, Строка, Контекст);
			ОбнулитьКоличествоКлючейДоступа(Строка, Контекст);
		Иначе
			Если Контекст.ЭтоПовторноеОбновлениеПрогресса Тогда
				СтрокиОбновленияКоличестваКлючейДоступа.Добавить(Строка);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
	
	Индекс = Контекст.ДобавленныеСтроки.Количество() - 1;
	Пока Индекс >= 0 Цикл
		Если Не ЗначениеЗаполнено(Контекст.ДобавленныеСтроки[Индекс].ИмяТаблицы) Тогда
			Контекст.ДобавленныеСтроки.Удалить(Индекс);
		КонецЕсли;
		Индекс = Индекс - 1;
	КонецЦикла;
	
	Если Не Контекст.ЭтоПовторноеОбновлениеПрогресса Тогда
		Для Каждого ОписаниеСтроки Из СтрокиСписков Цикл
			СтрокиОбновленияКоличестваЭлементов.Добавить(ОписаниеСтроки.Значение);
			СтрокиОбновленияКоличестваКлючейДоступа.Добавить(ОписаниеСтроки.Значение);
		КонецЦикла;
	КонецЕсли;
	
	Если Контекст.РассчитыватьПоКоличествуДанных Тогда
		КоличествоКлючей = РезультатыЗапроса[5].Выгрузить()[0].Количество;
		Если Контекст.ЭтоПовторноеОбновлениеПрогресса
		   И Контекст.ХранимыеДанные.КоличествоКлючей <> КоличествоКлючей Тогда
			
			Для Каждого ОписаниеСтроки Из СтрокиСписков Цикл
				Если СтрокиОбновленияКоличестваКлючейДоступа.Найти(ОписаниеСтроки.Значение) = Неопределено Тогда
					СтрокиОбновленияКоличестваКлючейДоступа.Добавить(ОписаниеСтроки.Значение);
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		Контекст.ХранимыеДанные.КоличествоКлючей = КоличествоКлючей;
	Иначе
		Контекст.ХранимыеДанные.КоличествоКлючей = 0;
	КонецЕсли;
	
	Если Контекст.РассчитыватьПоКоличествуДанных Тогда
		ТекущийКонтекст = Новый Структура;
		ТекущийКонтекст.Вставить("ДобавленныеСтроки",                       Контекст.ДобавленныеСтроки);
		ТекущийКонтекст.Вставить("ИзмененныеСтроки",                        Контекст.ИзмененныеСтроки);
		ТекущийКонтекст.Вставить("СвойстваСписков",                         СвойстваСписков);
		ТекущийКонтекст.Вставить("СтрокиСписков",                           СтрокиСписков);
		ТекущийКонтекст.Вставить("ДействующиеПараметры",                    ДействующиеПараметры);
		ТекущийКонтекст.Вставить("СтрокиОбновленияКоличестваЭлементов",     СтрокиОбновленияКоличестваЭлементов);
		ТекущийКонтекст.Вставить("СтрокиОбновленияКоличестваКлючейДоступа", СтрокиОбновленияКоличестваКлючейДоступа);
		ТекущийКонтекст.Вставить("ИдентификаторыТаблиц",                    ИдентификаторыТаблиц);
		ТекущийКонтекст.Вставить("ХранимыеДанные",                          Контекст.ХранимыеДанные);
		ВсегоОбновлено = 100;
		РассчитатьВсегоОбновленоПоКоличествуДанных(ТекущийКонтекст, ВсегоОбновлено);
	Иначе
		Если Контекст.ХранимыеДанные.Свойство("КоличествоЭлементовПоСпискам") Тогда
			Контекст.ХранимыеДанные.Удалить("КоличествоЭлементовПоСпискам");
			Контекст.ХранимыеДанные.Удалить("КоличествоКлючейДоступаПоСпискам");
		КонецЕсли;
		ВсегоКоличество = ИдентификаторыТаблиц.Количество();
		Если СтрокиСписков.Количество() > ВсегоКоличество Тогда
			ВсегоКоличество = СтрокиСписков.Количество();
		КонецЕсли;
		ВсегоОбновлено = (100 + 100) * (ВсегоКоличество - СтрокиСписков.Количество());
		Для Каждого КлючИЗначение Из СтрокиСписков Цикл
			Строка = КлючИЗначение.Значение;
			ВсегоОбновлено = ВсегоОбновлено + Строка.ОбработаноЭлементов + Строка.ОбработаноКлючейДоступа;
		КонецЦикла;
		ВсегоОбновлено = ВсегоОбновлено / 2 / ВсегоКоличество;
	КонецЕсли;
	Если ВсегоОбновлено > 100 Тогда
		ВсегоОбновлено = 100;
	КонецЕсли;
	Контекст.ВсегоОбновлено = Цел(ВсегоОбновлено);
	
	ВремяОбновления = ТекущаяДатаСеанса() - ДатаНачалаОбновленияПрогресса;
	
	Если Контекст.ЭтоПовторноеОбновлениеПрогресса
	   И ВремяОбновления > Контекст.ПериодОбновленияПрогресса Тогда
		
		Контекст.ПериодОбновленияПрогресса = ВремяОбновления;
	Иначе
		Контекст.Удалить("ПериодОбновленияПрогресса");
	КонецЕсли;
	
	Если Не Контекст.ЭтоПовторноеОбновлениеПрогресса И ВремяОбновления > 60 Тогда
		Контекст.АвтообновлениеПрогресса = Ложь;
	Иначе
		Контекст.Удалить("АвтообновлениеПрогресса");
	КонецЕсли;
	
КонецПроцедуры

Процедура ЗаполнитьДолиОбработанных(Строка, ОписаниеОбновления, СвойстваСписка, ЭтоОбработкаЭлементов, Контекст)
	
	Для Каждого СвойстваОбновления Из ОписаниеОбновления.Строки Цикл
		Если ЭтоОбработкаЭлементов Тогда
			Если СвойстваОбновления.ДляВнешнихПользователей Тогда
				ИмяПоляРазмераЗадания    = "РазмерЗаданияОбновленияЭлементовДляВнешнихПользователей";
				ИмяПоляПараметровЗадания = "ПоследнийОбновленныйЭлементДляВнешнихПользователей";
				ИмяПоляДоли              = "ДоляОбработанныхЭлементовДляВнешнихПользователей";
			Иначе
				ИмяПоляРазмераЗадания    = "РазмерЗаданияОбновленияЭлементов";
				ИмяПоляПараметровЗадания = "ПоследнийОбновленныйЭлемент";
				ИмяПоляДоли              = "ДоляОбработанныхЭлементовДляПользователей";
			КонецЕсли;
		Иначе
			Если СвойстваОбновления.ДляВнешнихПользователей Тогда
				ИмяПоляРазмераЗадания    = "РазмерЗаданияОбновленияКлючейДоступаДляВнешнихПользователей";
				ИмяПоляПараметровЗадания = "ПоследнийОбновленныйКлючДоступаДляВнешнихПользователей";
				ИмяПоляДоли              = "ДоляОбработанныхКлючейДоступаДляВнешнихПользователей";
			Иначе
				ИмяПоляРазмераЗадания    = "РазмерЗаданияОбновленияКлючейДоступа";
				ИмяПоляПараметровЗадания = "ПоследнийОбновленныйКлючДоступа";
				ИмяПоляДоли              = "ДоляОбработанныхКлючейДоступаДляПользователей";
			КонецЕсли;
		КонецЕсли;
		РазмерЗаданияОсновнаяЗапись = СвойстваСписка[ИмяПоляРазмераЗадания];
		РазмерЗаданияНовыеЗаписи = 0;
		Для Каждого СвойстваЗадания Из СвойстваОбновления.Строки Цикл
			Если СвойстваЗадания.ЭтоОсновнаяЗапись Тогда
				Если СвойстваЗадания.МаксимальнаяДатаИзменения > Строка.ПоследнееОбновление Тогда
					ОбновитьЗначениеВСтроке(Строка.ПоследнееОбновление,
						СвойстваЗадания.МаксимальнаяДатаИзменения, Строка, Контекст);
				КонецЕсли;
				РазмерЗаданияОсновнаяЗапись = СвойстваЗадания.РазмерЗадания;
			Иначе
				Если СвойстваЗадания.МинимальнаяДатаИзменения < Строка.ПервоеПланированиеОбновления Тогда
					ОбновитьЗначениеВСтроке(Строка.ПервоеПланированиеОбновления,
						СвойстваЗадания.МинимальнаяДатаИзменения, Строка, Контекст);
				КонецЕсли;
				РазмерЗаданияНовыеЗаписи = СвойстваЗадания.РазмерЗадания;
			КонецЕсли;
		КонецЦикла;
		СвойстваСписка[ИмяПоляРазмераЗадания] = РазмерЗаданияОсновнаяЗапись;
		Если РазмерЗаданияНовыеЗаписи >= РазмерЗаданияОсновнаяЗапись Тогда
			СвойстваСписка[ИмяПоляПараметровЗадания] = Истина;
			ТекущийРазмерЗадания = РазмерЗаданияНовыеЗаписи;
		Иначе
			ТекущийРазмерЗадания = РазмерЗаданияОсновнаяЗапись;
		КонецЕсли;
		Если ТекущийРазмерЗадания = 1 Тогда
			Строка[ИмяПоляДоли] = 0.99;
		ИначеЕсли ТекущийРазмерЗадания = 2 Тогда
			Строка[ИмяПоляДоли] = 0.90;
		Иначе
			Строка[ИмяПоляДоли] = 0.00;
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Функция ОбработаноПоДолям(ДоляОбработанныхДляПользователей, ДоляОбработанныхДляВнешнихПользователей, ИмяТаблицы, ДействующиеПараметры)
	
	ЕстьКлючиДляПользователей = Ложь;
	ЕстьКлючиДляВнешнихПользователей = Ложь;
	Версии = ДействующиеПараметры.ВерсииОграниченийСписков.Получить(ИмяТаблицы);
	Если ЗначениеЗаполнено(Версии) Тогда
		ЕстьКлючиДляПользователей        = ЗначениеЗаполнено(СтрПолучитьСтроку(Версии, 1));
		ЕстьКлючиДляВнешнихПользователей = ЗначениеЗаполнено(СтрПолучитьСтроку(Версии, 2));
	КонецЕсли;
	
	Если ДоляОбработанныхДляПользователей < 1 Тогда
		ЕстьКлючиДляПользователей = Истина;
	КонецЕсли;
	Если ДоляОбработанныхДляВнешнихПользователей < 1 Тогда
		ЕстьКлючиДляВнешнихПользователей = Истина;
	КонецЕсли;
	
	Если ЕстьКлючиДляПользователей И ЕстьКлючиДляВнешнихПользователей Тогда
		Обработано = Цел((ДоляОбработанныхДляПользователей
					+ ДоляОбработанныхДляВнешнихПользователей) / 2 * 100);
		
	ИначеЕсли ЕстьКлючиДляПользователей Тогда
		Обработано = Цел(ДоляОбработанныхДляПользователей * 100);
	Иначе
		Обработано = Цел(ДоляОбработанныхДляВнешнихПользователей * 100);
	КонецЕсли;
	
	Возврат Обработано;
	
КонецФункции

Процедура ОбнулитьКоличествоЭлементов(Строка, Контекст);
	
	ОбновитьЗначениеВСтроке(Строка.КоличествоЭлементов, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоОбработанныхЭлементов, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоЭлементовДляПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоЭлементовДляВнешнихПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоОставшихсяЭлементовДляПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.ДоляОставшихсяЭлементовДляПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоОставшихсяЭлементовДляВнешнихПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.ДоляОставшихсяЭлементовДляВнешнихПользователей, 0, Строка, Контекст);
	
КонецПроцедуры

Процедура ОбнулитьКоличествоКлючейДоступа(Строка, Контекст);
	
	ОбновитьЗначениеВСтроке(Строка.КоличествоКлючейДоступа, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоОбработанныхКлючейДоступа, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоКлючейДоступаДляПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоКлючейДоступаДляВнешнихПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоОставшихсяКлючейДоступаДляПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.ДоляОставшихсяКлючейДоступаДляПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.КоличествоОставшихсяКлючейДоступаДляВнешнихПользователей, 0, Строка, Контекст);
	ОбновитьЗначениеВСтроке(Строка.ДоляОставшихсяКлючейДоступаДляВнешнихПользователей, 0, Строка, Контекст);
	
КонецПроцедуры

Процедура ДобавитьНовуюСтрокуСписка(Контекст, Список, ИмяТаблицы)
	
	Строка = Новый Структура;
	Строка.Вставить("Список", Список);
	Строка.Вставить("СписокПредставление", Строка(Список));
	Строка.Вставить("ИмяТаблицы", ИмяТаблицы);
	Строка.Вставить("ОбработаноЭлементов", 0);
	Строка.Вставить("ОбработаноКлючейДоступа", 0);
	Строка.Вставить("КоличествоЭлементов", 0);
	Строка.Вставить("КоличествоКлючейДоступа", 0);
	Строка.Вставить("КоличествоОбработанныхЭлементов", 0);
	Строка.Вставить("КоличествоОбработанныхКлючейДоступа", 0);
	Строка.Вставить("ПоследнееОбновление", '00010101');
	Строка.Вставить("ПервоеПланированиеОбновления", МаксимальнаяДата());
	
	Строка.Вставить("КоличествоЭлементовДляПользователей", 0);
	Строка.Вставить("ДоляОбработанныхЭлементовДляПользователей", 1);
	Строка.Вставить("КоличествоЭлементовДляВнешнихПользователей", 0);
	Строка.Вставить("ДоляОбработанныхЭлементовДляВнешнихПользователей", 1);
	Строка.Вставить("КоличествоКлючейДоступаДляПользователей", 0);
	Строка.Вставить("ДоляОбработанныхКлючейДоступаДляПользователей", 1);
	Строка.Вставить("КоличествоКлючейДоступаДляВнешнихПользователей", 0);
	Строка.Вставить("ДоляОбработанныхКлючейДоступаДляВнешнихПользователей", 1);
	
	Строка.Вставить("КоличествоОставшихсяЭлементовДляПользователей", 0);
	Строка.Вставить("ДоляОставшихсяЭлементовДляПользователей", 0);
	Строка.Вставить("КоличествоОставшихсяЭлементовДляВнешнихПользователей", 0);
	Строка.Вставить("ДоляОставшихсяЭлементовДляВнешнихПользователей", 0);
	Строка.Вставить("КоличествоОставшихсяКлючейДоступаДляПользователей", 0);
	Строка.Вставить("ДоляОставшихсяКлючейДоступаДляПользователей", 0);
	Строка.Вставить("КоличествоОставшихсяКлючейДоступаДляВнешнихПользователей", 0);
	Строка.Вставить("ДоляОставшихсяКлючейДоступаДляВнешнихПользователей", 0);
	
	Контекст.ХранимыеДанные.СтрокиСписков.Вставить(Список, Строка);
	ДобавленныеСтроки = Контекст.ДобавленныеСтроки; // Массив
	ДобавленныеСтроки.Добавить(Строка);
	
	Свойства = Новый Структура;
	Свойства.Вставить("РазмерЗаданияОбновленияЭлементов", 0);
	Свойства.Вставить("ПоследнийОбновленныйЭлемент", Null);
	Свойства.Вставить("РазмерЗаданияОбновленияКлючейДоступа", 0);
	Свойства.Вставить("ПоследнийОбновленныйКлючДоступа", Null);
	
	Свойства.Вставить("РазмерЗаданияОбновленияЭлементовДляВнешнихПользователей", 0);
	Свойства.Вставить("ПоследнийОбновленныйЭлементДляВнешнихПользователей", Null);
	Свойства.Вставить("РазмерЗаданияОбновленияКлючейДоступаДляВнешнихПользователей", 0);
	Свойства.Вставить("ПоследнийОбновленныйКлючДоступаДляВнешнихПользователей", Null);
	
	Контекст.ХранимыеДанные.СвойстваСписков.Вставить(Список, Свойства);
	
КонецПроцедуры

// Параметры:
//  Контекст - Структура:
//   * ДобавленныеСтроки - Массив
//   * ИзмененныеСтроки - Соответствие
//   * СвойстваСписков - Соответствие
//   * СтрокиСписков - Соответствие
//   * ДействующиеПараметры - см. ДействующиеПараметрыОграниченияДоступа
//   * СтрокиОбновленияКоличестваЭлементов - Массив
//   * СтрокиОбновленияКоличестваКлючейДоступа - Массив
//   * ИдентификаторыТаблиц - см. ОбщегоНазначения.ИдентификаторыОбъектовМетаданных
//   * ХранимыеДанные - см. НовыеХранимыеДанныеОбновленияПрогресса
//   * ТипыТаблицПоИменам - Соответствие
//  
//  ВсегоОбновлено - Число
//
Процедура РассчитатьВсегоОбновленоПоКоличествуДанных(Контекст, ВсегоОбновлено)
	
	ТипыТаблицПоИменам = УправлениеДоступомСлужебныйПовтИсп.СинтаксисЯзыка().ТипыТаблиц.ПоИменам;
	Контекст.Вставить("ТипыТаблицПоИменам", ТипыТаблицПоИменам);
	
	Если Не Контекст.ХранимыеДанные.Свойство("КоличествоЭлементовПоСпискам") Тогда
		ЗаполнитьКоличествоЭлементовИКлючейДоступаПоСпискам(Контекст);
	КонецЕсли;
	
	ОбновитьКоличествоЭлементовИКлючейДоступа(Контекст);
	
	КоличествоЭлементовПоСпискам     = Контекст.ХранимыеДанные.КоличествоЭлементовПоСпискам;
	КоличествоКлючейДоступаПоСпискам = Контекст.ХранимыеДанные.КоличествоКлючейДоступаПоСпискам;
	
	ОбщееКоличествоЭлементов = 0;
	Для Каждого КлючИЗначение Из КоличествоЭлементовПоСпискам Цикл
		ОбщееКоличествоЭлементов = ОбщееКоличествоЭлементов + КлючИЗначение.Значение;
	КонецЦикла;
	ОбщееКоличествоЭлементов = ?(ОбщееКоличествоЭлементов = 0, 100, ОбщееКоличествоЭлементов);
	
	ОбщееКоличествоКлючейДоступа = 0;
	Для Каждого КлючИЗначение Из КоличествоКлючейДоступаПоСпискам Цикл
		ОбщееКоличествоКлючейДоступа = ОбщееКоличествоКлючейДоступа + КлючИЗначение.Значение;
	КонецЦикла;
	ОбщееКоличествоКлючейДоступа = ?(ОбщееКоличествоКлючейДоступа = 0, 100, ОбщееКоличествоКлючейДоступа);
	
	ВсегоОбновленоЭлементов = 0;
	ВсегоОбновленоКлючейДоступа = 0;
	
	ДобавкаОбщегоКоличестваЭлементов = 0;
	ДобавкаОбщегоКоличестваКлючейДоступа = 0;
	
	ИменаТаблицСОбновлениемЭлементов     = Новый Соответствие;
	ИменаТаблицСОбновлениемКлючейДоступа = Новый Соответствие;
	
	Для Каждого КлючИЗначение Из Контекст.СтрокиСписков Цикл
		Строка = КлючИЗначение.Значение;
		
		КоличествоЭлементов = КоличествоЭлементовПоСпискам.Получить(КлючИЗначение.Значение.ИмяТаблицы);
		Если КоличествоЭлементов = Неопределено Тогда
			Если КоличествоЭлементовПоСпискам.Количество() = 0 Тогда
				Добавка = 1;
			Иначе
				Добавка = Цел(ОбщееКоличествоЭлементов / КоличествоЭлементовПоСпискам.Количество() / 10);
				Добавка = ?(Добавка = 0, 1, Добавка);
			КонецЕсли;
			ДобавкаОбщегоКоличестваЭлементов = ДобавкаОбщегоКоличестваЭлементов + Добавка;
			ВсегоОбновленоЭлементов = ВсегоОбновленоЭлементов + 100 * Добавка;
		Иначе
			ВсегоОбновленоЭлементов = ВсегоОбновленоЭлементов + Строка.ОбработаноЭлементов * КоличествоЭлементов;
			ИменаТаблицСОбновлениемЭлементов.Вставить(КлючИЗначение.Значение.ИмяТаблицы, Истина);
		КонецЕсли;
		
		КоличествоКлючейДоступа = КоличествоКлючейДоступаПоСпискам.Получить(КлючИЗначение.Значение.ИмяТаблицы);
		Если КоличествоКлючейДоступа = Неопределено Тогда
			Если КоличествоКлючейДоступаПоСпискам.Количество() = 0 Тогда
				Добавка = 1;
			Иначе
				Добавка = Цел(ОбщееКоличествоКлючейДоступа / КоличествоКлючейДоступаПоСпискам.Количество() / 10);
				Добавка = ?(Добавка = 0, 1, Добавка);
			КонецЕсли;
			ДобавкаОбщегоКоличестваКлючейДоступа = ДобавкаОбщегоКоличестваКлючейДоступа + Добавка;
			ВсегоОбновленоКлючейДоступа = ВсегоОбновленоКлючейДоступа + 100 * Добавка;
		Иначе
			ВсегоОбновленоКлючейДоступа = ВсегоОбновленоКлючейДоступа + Строка.ОбработаноКлючейДоступа * КоличествоКлючейДоступа;
			ИменаТаблицСОбновлениемКлючейДоступа.Вставить(КлючИЗначение.Значение.ИмяТаблицы, Истина);
		КонецЕсли;
	КонецЦикла;
	
	КоличествоОбновленныхЭлементов = 0;
	Для Каждого КлючИЗначение Из КоличествоЭлементовПоСпискам Цикл
		Если ИменаТаблицСОбновлениемЭлементов.Получить(КлючИЗначение.Ключ) = Неопределено Тогда
			КоличествоОбновленныхЭлементов = КоличествоОбновленныхЭлементов + КлючИЗначение.Значение;
		КонецЕсли;
	КонецЦикла;
	ВсегоОбновленоЭлементов = ВсегоОбновленоЭлементов + КоличествоОбновленныхЭлементов  * 100;
		
	КоличествоОбновленныхКлючейДоступа = 0;
	Для Каждого КлючИЗначение Из КоличествоКлючейДоступаПоСпискам Цикл
		Если ИменаТаблицСОбновлениемКлючейДоступа.Получить(КлючИЗначение.Ключ) = Неопределено Тогда
			КоличествоОбновленныхКлючейДоступа = КоличествоОбновленныхКлючейДоступа + КлючИЗначение.Значение;
		КонецЕсли;
	КонецЦикла;
	ВсегоОбновленоКлючейДоступа = ВсегоОбновленоКлючейДоступа + КоличествоОбновленныхКлючейДоступа * 100;
	
	ВсегоОбновленоЭлементов = ВсегоОбновленоЭлементов
		/ (ОбщееКоличествоЭлементов + ДобавкаОбщегоКоличестваЭлементов);
	
	ВсегоОбновленоКлючейДоступа = ВсегоОбновленоКлючейДоступа
		/ (ОбщееКоличествоКлючейДоступа + ДобавкаОбщегоКоличестваКлючейДоступа);
	
	ВсегоОбновлено = (ВсегоОбновленоЭлементов + ВсегоОбновленоКлючейДоступа) / 2;
	
КонецПроцедуры

Процедура ЗаполнитьКоличествоЭлементовИКлючейДоступаПоСпискам(Контекст)
	
	КоличествоЭлементовПоСпискам     = Новый Соответствие;
	КоличествоКлючейДоступаПоСпискам = Новый Соответствие;
	Контекст.ХранимыеДанные.Вставить("КоличествоЭлементовПоСпискам",     КоличествоЭлементовПоСпискам);
	Контекст.ХранимыеДанные.Вставить("КоличествоКлючейДоступаПоСпискам", КоличествоКлючейДоступаПоСпискам);
	
	ОписаниеЗапроса = Новый Структура("Запрос, ТекстыПакетаЗапросов", Новый Запрос, Новый Массив);
	
	ОбновляемыеТаблицы = Новый Соответствие;
	ИдентификаторыТаблиц = Контекст.ИдентификаторыТаблиц;
	Для Каждого Строка Из Контекст.СтрокиОбновленияКоличестваЭлементов Цикл
		Если ИдентификаторыТаблиц.Получить(Строка.ИмяТаблицы) = Строка.Список Тогда
			ОбновляемыеТаблицы.Вставить(Строка.ИмяТаблицы, Строка.Список);
		КонецЕсли;
	КонецЦикла;
	Для Каждого Строка Из Контекст.СтрокиОбновленияКоличестваКлючейДоступа Цикл
		Если ИдентификаторыТаблиц.Получить(Строка.ИмяТаблицы) = Строка.Список Тогда
			ОбновляемыеТаблицы.Вставить(Строка.ИмяТаблицы, Строка.Список);
		КонецЕсли;
	КонецЦикла;
	
	Индекс = 0;
	ИменаТаблиц = Новый Массив;
	Для Каждого КлючИЗначение Из Контекст.ИдентификаторыТаблиц Цикл
		Если ОбновляемыеТаблицы.Получить(КлючИЗначение.Ключ) = КлючИЗначение.Значение Тогда
			Продолжить;
		КонецЕсли;
		ИменаТаблиц.Добавить(КлючИЗначение.Ключ);
		Строка = Новый Структура("Список, ИмяТаблицы", КлючИЗначение.Значение, КлючИЗначение.Ключ);
		ДобавитьТекстЗапросаКоличестваЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, Ложь);
		ДобавитьТекстЗапросаКоличестваЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, Истина);
		ДобавитьТекстЗапросаКоличестваКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, Ложь);
		ДобавитьТекстЗапросаКоличестваКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, Истина);
	КонецЦикла;
	
	Если ОписаниеЗапроса.ТекстыПакетаЗапросов.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	РезультатыЗапроса = ВыполнитьПакетЗапросовПоЧастям(ОписаниеЗапроса);
	
	Индекс = 0;
	Для Каждого ИмяТаблицы Из ИменаТаблиц Цикл
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		КоличествоЭлементовДляПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		КоличествоЭлементовДляВнешнихПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		КоличествоЭлементовПоСпискам[ИмяТаблицы] = КоличествоЭлементовДляПользователей
			+ КоличествоЭлементовДляВнешнихПользователей;
		
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		КоличествоКлючейДоступаДляПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		КоличествоКлючейДоступаДляВнешнихПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		КоличествоКлючейДоступаПоСпискам[ИмяТаблицы] = КоличествоКлючейДоступаДляПользователей
			+ КоличествоКлючейДоступаДляВнешнихПользователей;
	КонецЦикла;
	
КонецПроцедуры

Процедура ОбновитьКоличествоЭлементовИКлючейДоступа(Контекст)
	
	ОписаниеЗапроса = Новый Структура("Запрос, ТекстыПакетаЗапросов", Новый Запрос, Новый Массив);
	Индекс = 0;
	Для Каждого Строка Из Контекст.СтрокиОбновленияКоличестваЭлементов Цикл
		ДобавитьТекстЗапросаКоличестваЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, Ложь);
		ДобавитьТекстЗапросаКоличестваЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, Истина);
		ДобавитьТекстЗапросаКоличестваОставшихсяЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, Ложь);
		ДобавитьТекстЗапросаКоличестваОставшихсяЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, Истина);
	КонецЦикла;
	Для Каждого Строка Из Контекст.СтрокиОбновленияКоличестваКлючейДоступа Цикл
		ДобавитьТекстЗапросаКоличестваКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, Ложь);
		ДобавитьТекстЗапросаКоличестваКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, Истина);
		ДобавитьТекстЗапросаКоличестваОставшихсяКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, Контекст, Ложь);
		ДобавитьТекстЗапросаКоличестваОставшихсяКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, Контекст, Истина);
	КонецЦикла;
	
	Если ОписаниеЗапроса.ТекстыПакетаЗапросов.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	РезультатыЗапроса = ВыполнитьПакетЗапросовПоЧастям(ОписаниеЗапроса);

	КоличествоЭлементовПоСпискам     = Контекст.ХранимыеДанные.КоличествоЭлементовПоСпискам;
	КоличествоКлючейДоступаПоСпискам = Контекст.ХранимыеДанные.КоличествоКлючейДоступаПоСпискам;
	ВерсииОграниченийСписков         = Контекст.ДействующиеПараметры.ВерсииОграниченийСписков;
	ТипыТаблицПоИменам               = Контекст.ТипыТаблицПоИменам;
	
	Индекс = 0;
	Для Каждого Строка Из Контекст.СтрокиОбновленияКоличестваЭлементов Цикл
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		Строка.КоличествоЭлементовДляПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		Строка.КоличествоЭлементовДляВнешнихПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		КоличествоЭлементов = Строка.КоличествоЭлементовДляПользователей + Строка.КоличествоЭлементовДляВнешнихПользователей;
		Если ЗначениеЗаполнено(Строка.ИмяТаблицы) Тогда
			КоличествоЭлементовПоСпискам.Вставить(Строка.ИмяТаблицы, КоличествоЭлементов);
		КонецЕсли;
		
		Если КоличествоЭлементов = 0 Тогда
			ОбнулитьКоличествоЭлементов(Строка, Контекст);
			ОбновитьЗначениеВСтроке(Строка.ОбработаноЭлементов, 100, Строка, Контекст);
			Индекс = Индекс + 2;
		Иначе
			КоличествоОбработанныхЭлементов = 0;
			СвойстваСписка = Контекст.СвойстваСписков.Получить(Строка.Список);
			ЕстьКлючиДляПользователей = Ложь;
			ЕстьКлючиДляВнешнихПользователей = Ложь;
			Версии = ВерсииОграниченийСписков.Получить(Строка.ИмяТаблицы);
			Если ЗначениеЗаполнено(Версии) Тогда
				ЕстьКлючиДляПользователей        = ЗначениеЗаполнено(СтрПолучитьСтроку(Версии, 1));
				ЕстьКлючиДляВнешнихПользователей = ЗначениеЗаполнено(СтрПолучитьСтроку(Версии, 2));
			КонецЕсли;
			СоставИмени = СтрРазделить(Строка.ИмяТаблицы, ".", Ложь);
			СвойстваТипа = ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
			Для ВидПользователей = 0 По 1 Цикл
				Если ВидПользователей = 0 Тогда
					ПоследнийОбновленныйЭлемент = СвойстваСписка.ПоследнийОбновленныйЭлемент;
				Иначе
					ПоследнийОбновленныйЭлемент = СвойстваСписка.ПоследнийОбновленныйЭлементДляВнешнихПользователей;
				КонецЕсли;
				Если ПоследнийОбновленныйЭлемент = Null Или ПоследнийОбновленныйЭлемент = Истина Тогда
					КоличествоОставшихсяЭлементов = -1;
				КонецЕсли;
				Если ПоследнийОбновленныйЭлемент <> Null Тогда
					Если ВидПользователей = 0 Тогда
						ЕстьКлючиДляПользователей = Истина;
					Иначе
						ЕстьКлючиДляВнешнихПользователей = Истина;
					КонецЕсли;
					Если ПоследнийОбновленныйЭлемент <> Истина Тогда
						Выборка = РезультатыЗапроса[Индекс].Выбрать();
						КоличествоОставшихсяЭлементов = ?(Выборка.Следующий(), Выборка.Количество, 0);
					КонецЕсли;
				КонецЕсли;
				Если КоличествоОставшихсяЭлементов = -1 Тогда
					Если ВидПользователей = 0 Тогда
						Строка.ДоляОставшихсяЭлементовДляПользователей = 0;
					Иначе
						Строка.ДоляОставшихсяЭлементовДляВнешнихПользователей = 0;
					КонецЕсли;
					КоличествоОставшихсяЭлементов = 0;
				КонецЕсли;
				Если ВидПользователей = 0 Тогда
					Если Не ЕстьКлючиДляПользователей Тогда
						Строка.КоличествоОставшихсяЭлементовДляПользователей = 0;
						Строка.КоличествоЭлементовДляПользователей = 0;
					ИначеЕсли ТипЗнч(КоличествоОставшихсяЭлементов) = Тип("Число") Тогда
						Строка.КоличествоОставшихсяЭлементовДляПользователей
							= Цел(Строка.КоличествоЭлементовДляПользователей * (1 - Строка.ДоляОбработанныхЭлементовДляПользователей
								- Строка.ДоляОставшихсяЭлементовДляПользователей) + 0.99)
							+ Цел(КоличествоОставшихсяЭлементов * Строка.ДоляОставшихсяЭлементовДляПользователей);
					КонецЕсли;
					КоличествоОбработанныхЭлементов = КоличествоОбработанныхЭлементов
						+ (Строка.КоличествоЭлементовДляПользователей
							- Строка.КоличествоОставшихсяЭлементовДляПользователей);
				Иначе
					Если Не ЕстьКлючиДляВнешнихПользователей Тогда
						Строка.КоличествоОставшихсяЭлементовДляВнешнихПользователей = 0;
						Строка.КоличествоЭлементовДляВнешнихПользователей = 0;
					ИначеЕсли ТипЗнч(КоличествоОставшихсяЭлементов) = Тип("Число") Тогда
						Строка.КоличествоОставшихсяЭлементовДляВнешнихПользователей
							= Цел(Строка.КоличествоЭлементовДляВнешнихПользователей * (1 - Строка.ДоляОбработанныхЭлементовДляВнешнихПользователей
								- Строка.ДоляОставшихсяЭлементовДляВнешнихПользователей) + 0.99)
							+ Цел(КоличествоОставшихсяЭлементов * Строка.ДоляОставшихсяЭлементовДляВнешнихПользователей);
					КонецЕсли;
					КоличествоОбработанныхЭлементов = КоличествоОбработанныхЭлементов
						+ (Строка.КоличествоЭлементовДляВнешнихПользователей
							- Строка.КоличествоОставшихсяЭлементовДляВнешнихПользователей);
				КонецЕсли;
				Индекс = Индекс + 1;
			КонецЦикла;
			КоличествоЭлементов = Строка.КоличествоЭлементовДляПользователей + Строка.КоличествоЭлементовДляВнешнихПользователей;
			Если ЕстьКлючиДляПользователей И ЕстьКлючиДляВнешнихПользователей И СвойстваТипа.ЭтоСсылочныйТип Тогда
				КоличествоЭлементов             = КоличествоЭлементов / 2;
				КоличествоОбработанныхЭлементов = КоличествоОбработанныхЭлементов / 2;
			КонецЕсли;
			ОбновитьЗначениеВСтроке(Строка.КоличествоЭлементов, КоличествоЭлементов, Строка, Контекст);
			ОбновитьЗначениеВСтроке(Строка.КоличествоОбработанныхЭлементов,
				Цел(КоличествоОбработанныхЭлементов), Строка, Контекст);
			Если Строка.КоличествоОбработанныхЭлементов > Строка.КоличествоЭлементов Тогда
				ОбновитьЗначениеВСтроке(Строка.КоличествоОбработанныхЭлементов,
					Строка.КоличествоЭлементов, Строка, Контекст);
			КонецЕсли;
			ОбновитьЗначениеВСтроке(Строка.ОбработаноЭлементов, ?(Строка.КоличествоЭлементов = 0, 100,
				Цел(Строка.КоличествоОбработанныхЭлементов / Строка.КоличествоЭлементов * 100)), Строка, Контекст);
		КонецЕсли;
	КонецЦикла;
	
	Для Каждого Строка Из Контекст.СтрокиОбновленияКоличестваКлючейДоступа Цикл
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		КоличествоКлючейДоступаДляПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		Выборка = РезультатыЗапроса[Индекс].Выбрать();
		КоличествоКлючейДоступаДляВнешнихПользователей = ?(Выборка.Следующий(), Выборка.Количество, 0);
		Индекс = Индекс + 1;
		
		КоличествоКлючейДоступа = КоличествоКлючейДоступаДляПользователей + КоличествоКлючейДоступаДляВнешнихПользователей;
		ОбновитьЗначениеВСтроке(Строка.КоличествоКлючейДоступа, КоличествоКлючейДоступа, Строка, Контекст);
		Если ЗначениеЗаполнено(Строка.ИмяТаблицы) Тогда
			КоличествоКлючейДоступаПоСпискам.Вставить(Строка.ИмяТаблицы, КоличествоКлючейДоступа);
		КонецЕсли;
		
		Если Строка.КоличествоКлючейДоступа = 0 Тогда
			ОбнулитьКоличествоКлючейДоступа(Строка, Контекст);
			ОбновитьЗначениеВСтроке(Строка.ОбработаноКлючейДоступа, 100, Строка, Контекст);
			Индекс = Индекс + 2;
		Иначе
			КоличествоОбработанныхКлючейДоступа = 0;
			СвойстваСписка = Контекст.СвойстваСписков.Получить(Строка.Список);
			Для ВидПользователей = 0 По 1 Цикл
				Если ВидПользователей = 0 Тогда
					ПоследнийОбновленныйКлючДоступа = СвойстваСписка.ПоследнийОбновленныйКлючДоступа;
				Иначе
					ПоследнийОбновленныйКлючДоступа = СвойстваСписка.ПоследнийОбновленныйКлючДоступаДляВнешнихПользователей;
				КонецЕсли;
				Если ПоследнийОбновленныйКлючДоступа = Null Или ПоследнийОбновленныйКлючДоступа = Истина Тогда
					КоличествоОставшихсяКлючейДоступа = -1;
				Иначе
					Выборка = РезультатыЗапроса[Индекс].Выбрать();
					КоличествоОставшихсяКлючейДоступа = ?(Выборка.Следующий(), Выборка.Количество, 0);
				КонецЕсли;
				Если КоличествоОставшихсяКлючейДоступа = -1 Тогда
					Если ВидПользователей = 0 Тогда
						Строка.ДоляОставшихсяКлючейДоступаДляПользователей = 0;
					Иначе
						Строка.ДоляОставшихсяКлючейДоступаДляВнешнихПользователей = 0;
					КонецЕсли;
					КоличествоОставшихсяКлючейДоступа = 0;
				КонецЕсли;
				Если ВидПользователей = 0 Тогда
					Если ТипЗнч(КоличествоОставшихсяКлючейДоступа) = Тип("Число") Тогда
						Строка.КоличествоКлючейДоступаДляПользователей = КоличествоКлючейДоступаДляПользователей;
						Строка.КоличествоОставшихсяКлючейДоступаДляПользователей
							= Цел(Строка.КоличествоКлючейДоступаДляПользователей * (1 - Строка.ДоляОбработанныхКлючейДоступаДляПользователей
								- Строка.ДоляОставшихсяКлючейДоступаДляПользователей) + 0.99)
							+ Цел(КоличествоОставшихсяКлючейДоступа * Строка.ДоляОставшихсяКлючейДоступаДляПользователей);
					КонецЕсли;
					КоличествоОбработанныхКлючейДоступа = КоличествоОбработанныхКлючейДоступа
						+ (Строка.КоличествоКлючейДоступаДляПользователей
							- Строка.КоличествоОставшихсяКлючейДоступаДляПользователей);
				Иначе
					Если ТипЗнч(КоличествоОставшихсяКлючейДоступа) = Тип("Число") Тогда
						Строка.КоличествоКлючейДоступаДляВнешнихПользователей = КоличествоКлючейДоступаДляВнешнихПользователей;
						Строка.КоличествоОставшихсяКлючейДоступаДляВнешнихПользователей
							= Цел(Строка.КоличествоКлючейДоступаДляВнешнихПользователей * (1 - Строка.ДоляОбработанныхКлючейДоступаДляВнешнихПользователей
								- Строка.ДоляОставшихсяКлючейДоступаДляВнешнихПользователей) + 0.99)
							+ Цел(КоличествоОставшихсяКлючейДоступа * Строка.ДоляОставшихсяКлючейДоступаДляВнешнихПользователей);
					КонецЕсли;
					КоличествоОбработанныхКлючейДоступа = КоличествоОбработанныхКлючейДоступа
						+ (Строка.КоличествоКлючейДоступаДляВнешнихПользователей
							- Строка.КоличествоОставшихсяКлючейДоступаДляВнешнихПользователей);
				КонецЕсли;
				Индекс = Индекс + 1;
			КонецЦикла;
			ОбновитьЗначениеВСтроке(Строка.КоличествоОбработанныхКлючейДоступа,
				Цел(КоличествоОбработанныхКлючейДоступа), Строка, Контекст);
			Если Строка.КоличествоОбработанныхКлючейДоступа > Строка.КоличествоКлючейДоступа Тогда
				ОбновитьЗначениеВСтроке(Строка.КоличествоОбработанныхКлючейДоступа,
					Строка.КоличествоКлючейДоступа, Строка, Контекст);
			КонецЕсли;
			ОбновитьЗначениеВСтроке(Строка.ОбработаноКлючейДоступа,
				Цел(Строка.КоличествоОбработанныхКлючейДоступа / Строка.КоличествоКлючейДоступа * 100), Строка, Контекст);
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

// Возвращаемое значение:
//  Массив из РезультатЗапроса
//
Функция ВыполнитьПакетЗапросовПоЧастям(ОписаниеЗапроса)
	
	РезультатыПакетаЗапросов = Новый Массив;
	
	ТекстыПорцииЗапросов = Новый Массив;
	Для Каждого ТекстЗапроса Из ОписаниеЗапроса.ТекстыПакетаЗапросов Цикл
		Если ТекстыПорцииЗапросов.Количество() = 200 Тогда
			// @skip-check query-in-loop - Порционная обработка данных
			ДобавитьРезультатыЗапроса(РезультатыПакетаЗапросов, ТекстыПорцииЗапросов, ОписаниеЗапроса);
			ТекстыПорцииЗапросов = Новый Массив;
		КонецЕсли;
		ТекстыПорцииЗапросов.Добавить(ТекстЗапроса);
	КонецЦикла;
	ДобавитьРезультатыЗапроса(РезультатыПакетаЗапросов, ТекстыПорцииЗапросов, ОписаниеЗапроса);
	
	Возврат РезультатыПакетаЗапросов;
	
КонецФункции

Процедура ДобавитьРезультатыЗапроса(РезультатыПакетаЗапросов, ТекстыПорцииЗапросов, ОписаниеЗапроса)
	
	Запрос = ОписаниеЗапроса.Запрос;
	
	Запрос.Текст = СтрСоединить(ТекстыПорцииЗапросов, ОбщегоНазначения.РазделительПакетаЗапросов());
	РезультатыЗапроса = Запрос.ВыполнитьПакет();
	Запрос.Текст = "";
	
	Для Каждого РезультатЗапроса Из РезультатыЗапроса Цикл
		РезультатыПакетаЗапросов.Добавить(РезультатЗапроса);
	КонецЦикла;
	
КонецПроцедуры

Процедура ОбновитьЗначениеВСтроке(СтароеЗначение, НовоеЗначение, Строка, Контекст)
	
	Если СтароеЗначение = НовоеЗначение Тогда
		Возврат;
	КонецЕсли;
	СтароеЗначение = НовоеЗначение;
	
	Если Контекст.ДобавленныеСтроки.Найти(Строка) = Неопределено
	   И Контекст.ИзмененныеСтроки.Получить(Строка) = Неопределено Тогда
		
		Контекст.ИзмененныеСтроки.Вставить(Строка.Список, Строка);
	КонецЕсли;
	
КонецПроцедуры

Процедура ДобавитьТекстЗапросаКоличестваЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, ДляВнешнихПользователей)
	
	Версии = Контекст.ДействующиеПараметры.ВерсииОграниченийСписков.Получить(Строка.ИмяТаблицы);
	
	Если ЗначениеЗаполнено(Версии)
	   И ЗначениеЗаполнено(Строка.ИмяТаблицы)
	 Или Строка.ИмяТаблицы = "Справочник.НаборыГруппДоступа" Тогда
		
		СоставИмени = СтрРазделить(Строка.ИмяТаблицы, ".", Ложь);
		СвойстваТипа = Контекст.ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
		Если СвойстваТипа.ЭтоСсылочныйТип Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	КОЛИЧЕСТВО(*) КАК Количество
			|ИЗ
			|	&ТекущаяТаблица КАК ТекущаяТаблица";
		Иначе
			ТекстЗапроса = ТекстЗапросаКоличестваЭлементовРегистра(Контекст, Строка, Индекс, ДляВнешнихПользователей);
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущаяТаблица", Строка.ИмяТаблицы);
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	0 КАК Количество";
	КонецЕсли;
	
	ОписаниеЗапроса.ТекстыПакетаЗапросов.Добавить(ТекстЗапроса);
	Индекс = Индекс + 1;
	
КонецПроцедуры

Функция ТекстЗапросаКоличестваЭлементовРегистра(Контекст, Строка, Индекс, ДляВнешнихПользователей)
	
	Если ДляВнешнихПользователей Тогда
		ДополнительныйКонтекст = Контекст.ДействующиеПараметры.ДополнительныйКонтекст.ДляВнешнихПользователей;
	Иначе
		ДополнительныйКонтекст = Контекст.ДействующиеПараметры.ДополнительныйКонтекст.ДляПользователей;
	КонецЕсли;
	Свойства = ДополнительныйКонтекст.СвойстваОграниченияСписков.Получить(Строка.ИмяТаблицы);
	
	Если Свойства = Неопределено
	 Или Свойства.ОпорныеПоля = Неопределено
	 Или Свойства.ОпорныеПоля.Используемые.Количество() = 0 Тогда
	
		Возврат
		"ВЫБРАТЬ
		|	0 КАК Количество";
	КонецЕсли;
	
	ПоляВыбора = "";
	Для Каждого ИмяПоля Из Свойства.ОпорныеПоля.Используемые Цикл
		ПоляВыбора = ПоляВыбора + ?(ПоляВыбора = "", "", ",
		|		") + "ТекущаяТаблица." + ИмяПоля;
	КонецЦикла;
	
	Если Свойства.ОпорныеПоля.Используемые.Количество() = 1 Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ &ПоляВыбора) КАК Количество
		|ИЗ
		|	&ТекущаяТаблица КАК ТекущаяТаблица";
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	КОЛИЧЕСТВО(*) КАК Количество
		|ИЗ
		|	(ВЫБРАТЬ РАЗЛИЧНЫЕ
		|		&ПоляВыбора КАК ПоляВыбора
		|	ИЗ
		|		&ТекущаяТаблица КАК ТекущаяТаблица) КАК Комбинации";
		ПоляВыбора = ТекстСОтступом(ПоляВыбора, "	");
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляВыбора", ПоляВыбора);
	
	Возврат ТекстЗапроса;
	
КонецФункции

Процедура ДобавитьТекстЗапросаКоличестваОставшихсяЭлементов(ОписаниеЗапроса, Строка, Индекс, Контекст, ДляВнешнихПользователей)
	
	СвойстваСписка = Контекст.СвойстваСписков.Получить(Строка.Список);
	СпискиСДатой = Контекст.ДействующиеПараметры.СпискиСДатой;
	
	Если ЗначениеЗаполнено(Строка.ИмяТаблицы) Тогда
		СоставИмени = СтрРазделить(Строка.ИмяТаблицы, ".", Ложь);
		СвойстваТипа = Контекст.ТипыТаблицПоИменам.Получить(ВРег(СоставИмени[0]));
	Иначе
		СвойстваСписка = Неопределено;
	КонецЕсли;
	
	Если СвойстваСписка = Неопределено Тогда
		ПараметрыЗадания = Неопределено;
		
	ИначеЕсли ДляВнешнихПользователей Тогда
		ПараметрыЗадания = СвойстваСписка.ПоследнийОбновленныйЭлементДляВнешнихПользователей;
	Иначе
		ПараметрыЗадания = СвойстваСписка.ПоследнийОбновленныйЭлемент;
	КонецЕсли;
	
	Если ТипЗнч(ПараметрыЗадания) <> Тип("ХранилищеЗначения") Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	НЕОПРЕДЕЛЕНО КАК Количество";
	Иначе
		ДолиКоличестваЭлементов = Новый Структура("Обработанных, Оставшихся", 0, 1);
		ПараметрыЗадания = ПараметрыЗадания.Получить();
		
		Если ТипЗнч(ПараметрыЗадания) = Тип("Структура")
		   И ПараметрыЗадания.Свойство("ПоследнийОбновленныйЭлемент")
		   И ТипЗнч(ПараметрыЗадания.ПоследнийОбновленныйЭлемент) = Тип("Структура")
		   И ПараметрыЗадания.ПоследнийОбновленныйЭлемент.Свойство("ВидКлючаДанных")
		   И ПорядокВидаКлючаДанных(ПараметрыЗадания.ПоследнийОбновленныйЭлемент.ВидКлючаДанных) <> Неопределено
		   И ПараметрыЗадания.ПоследнийОбновленныйЭлемент.Свойство("КлючДанных")
		   И ПараметрыЗадания.ПоследнийОбновленныйЭлемент.Свойство("ОбработатьУстаревшиеЭлементы")
		   И ТипЗнч(ПараметрыЗадания.ПоследнийОбновленныйЭлемент.ОбработатьУстаревшиеЭлементы) = Тип("Булево") Тогда
			
			ПоследнийОбновленныйЭлемент = ПараметрыЗадания.ПоследнийОбновленныйЭлемент;
		Иначе
			ПоследнийОбновленныйЭлемент = Новый Структура;
			ПоследнийОбновленныйЭлемент.Вставить("ВидКлючаДанных", "ЭлементыДанныхСУстаревшимиКлючами");
			ПоследнийОбновленныйЭлемент.Вставить("КлючДанных");
		КонецЕсли;
		
		УсловиеОтбора = "";
		Если СвойстваТипа.ЭтоСсылочныйТип Тогда
			КлючДанных = ПоследнийОбновленныйЭлемент.КлючДанных;
			Если СпискиСДатой.Получить(Строка.ИмяТаблицы) <> Неопределено Тогда
				
				Если ПоследнийОбновленныйЭлемент.Свойство("Дата")
				   И ТипЗнч(ПоследнийОбновленныйЭлемент.Дата) = Тип("Дата") Тогда
					
					ИмяПараметра = "ПоследняяДата" + Формат(Индекс, "ЧГ=");
					УсловиеОтбора = "ТекущаяТаблица.Дата <= &" + ИмяПараметра;
					ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, ПоследнийОбновленныйЭлемент.Дата);
				КонецЕсли;
				
			ИначеЕсли ЗначениеЗаполнено(КлючДанных) Тогда
				ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(КлючДанных));
				Если ОбъектМетаданных <> Неопределено
				   И ОбъектМетаданных.ПолноеИмя() = Строка.ИмяТаблицы Тогда
					
					ИмяПараметра = "ПоследняяСсылка" + Формат(Индекс, "ЧГ=");
					УсловиеОтбора = "ТекущаяТаблица.Ссылка > &" + ИмяПараметра;
					ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, КлючДанных);
				КонецЕсли;
			КонецЕсли;
			Если ЗначениеЗаполнено(УсловиеОтбора) Тогда
				ТекстЗапроса =
				"ВЫБРАТЬ
				|	КОЛИЧЕСТВО(*) КАК Количество
				|ИЗ
				|	&ТекущаяТаблица КАК ТекущаяТаблица
				|ГДЕ
				|	&УсловиеОтбора";
				ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", УсловиеОтбора);
			Иначе
				ТекстЗапроса =
				"ВЫБРАТЬ
				|	-1 КАК Количество";
			КонецЕсли;
			ЗаполнитьДолиКоличествоЭлементовСсылочногоТипа(ДолиКоличестваЭлементов,
				ПоследнийОбновленныйЭлемент, Строка.ИмяТаблицы);
		Иначе
			Если ДляВнешнихПользователей Тогда
				ДополнительныйКонтекст = Контекст.ДействующиеПараметры.ДополнительныйКонтекст.ДляВнешнихПользователей;
			Иначе
				ДополнительныйКонтекст = Контекст.ДействующиеПараметры.ДополнительныйКонтекст.ДляПользователей;
			КонецЕсли;
			СвойстваОграничения = ДополнительныйКонтекст.СвойстваОграниченияСписков.Получить(Строка.ИмяТаблицы);
			
			Если СвойстваОграничения <> Неопределено Тогда
				ИспользуемыеВариантыДоступа = ДополнительныйКонтекст.ОсновныеВариантыДоступа.Получить(Строка.ИмяТаблицы);
				Если ИспользуемыеВариантыДоступа = Неопределено Тогда
					СвойстваОграничения = Неопределено;
				Иначе
					СвойстваОграничения = Новый Структура(СвойстваОграничения);
					СвойстваОграничения.Вставить("ВариантДоступа", ИспользуемыеВариантыДоступа[0].ВариантДоступа);
				КонецЕсли;
			КонецЕсли;
			
			ЗаполнитьДолиКоличестваЭлементовРегистра(ДолиКоличестваЭлементов,
				ПоследнийОбновленныйЭлемент, СвойстваОграничения);
			
			ТекстЗапроса = ТекстЗапросаКоличестваОставшихсяЭлементовРегистра(ОписаниеЗапроса, Строка, Индекс,
				ДляВнешнихПользователей, ПоследнийОбновленныйЭлемент, СвойстваОграничения, СвойстваТипа);
		КонецЕсли;
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ТекущаяТаблица", Строка.ИмяТаблицы);
		
		Если ДляВнешнихПользователей Тогда
			Строка.ДоляОбработанныхЭлементовДляВнешнихПользователей = ДолиКоличестваЭлементов.Обработанных;
			Строка.ДоляОставшихсяЭлементовДляВнешнихПользователей   = ДолиКоличестваЭлементов.Оставшихся;
		Иначе
			Строка.ДоляОбработанныхЭлементовДляПользователей = ДолиКоличестваЭлементов.Обработанных;
			Строка.ДоляОставшихсяЭлементовДляПользователей   = ДолиКоличестваЭлементов.Оставшихся;
		КонецЕсли;
	КонецЕсли;
	
	ОписаниеЗапроса.ТекстыПакетаЗапросов.Добавить(ТекстЗапроса);
	Индекс = Индекс + 1;
	
КонецПроцедуры

Процедура ЗаполнитьДолиКоличествоЭлементовСсылочногоТипа(ДолиКоличестваЭлементов, Элемент, ИмяТаблицы)
	
	Если ИмяТаблицы = "Справочник.НаборыГруппДоступа" Тогда
		Если Элемент.ВидКлючаДанных = "НовыеНаборыИзОдногоПользователя" Тогда
			
			ДолиКоличестваЭлементов.Обработанных = 0.0;
			ДолиКоличестваЭлементов.Оставшихся   = 0.1;
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НаборыГруппДоступаНазначенныеПользователям" Тогда
			
			ДолиКоличестваЭлементов.Обработанных = 0.1;
			ДолиКоличестваЭлементов.Оставшихся   = 0.1;
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НаборыГруппПользователейНазначенныеПользователям" Тогда
			
			ДолиКоличестваЭлементов.Обработанных = 0.2;
			ДолиКоличестваЭлементов.Оставшихся   = 0.1;
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НовыеНаборыГруппСУстаревшимиПравами" Тогда
			
			ДолиКоличестваЭлементов.Обработанных = 0.3;
			ДолиКоличестваЭлементов.Оставшихся   = 0.2;
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НаборыГруппРазрешенныеПользователям" Тогда
			
			ДолиКоличестваЭлементов.Обработанных = 0.5;
			ДолиКоличестваЭлементов.Оставшихся   = 0.1;
			
		ИначеЕсли Элемент.ВидКлючаДанных = "НаборыГруппСУстаревшимиПравами" Тогда
			
			Если Элемент.ОбработатьУстаревшиеЭлементы Тогда
				ДолиКоличестваЭлементов.Обработанных = 0.6;
				ДолиКоличестваЭлементов.Оставшихся   = 0.3;
			Иначе
				ДолиКоличестваЭлементов.Обработанных = 0.6;
				ДолиКоличестваЭлементов.Оставшихся   = 0.4;
			КонецЕсли;
			
		Иначе // УстаревшиеЭлементы.
			ДолиКоличестваЭлементов.Обработанных = 0.9;
			ДолиКоличестваЭлементов.Оставшихся   = 0.1;
		КонецЕсли;
		Возврат;
	КонецЕсли;
	
	Если Элемент.ВидКлючаДанных = "ЭлементыСУстаревшимиКлючами" Тогда
		Если Элемент.ОбработатьУстаревшиеЭлементы Тогда
			ДолиКоличестваЭлементов.Обработанных = 0.0;
			ДолиКоличестваЭлементов.Оставшихся   = 0.9;
		Иначе
			ДолиКоличестваЭлементов.Обработанных = 0.0;
			ДолиКоличестваЭлементов.Оставшихся   = 1.0;
		КонецЕсли;
	Иначе // УстаревшиеЭлементы.
		ДолиКоличестваЭлементов.Обработанных = 0.9;
		ДолиКоличестваЭлементов.Оставшихся   = 0.1;
	КонецЕсли;
	
КонецПроцедуры

Функция ТекстЗапросаКоличестваОставшихсяЭлементовРегистра(ОписаниеЗапроса, Строка, Индекс,
			ДляВнешнихПользователей, ПоследнийОбновленныйЭлемент, СвойстваОграничения, СвойстваТипа)
	
	ВидКлючаДанных = ПоследнийОбновленныйЭлемент.ВидКлючаДанных;
	КлючДанных     = ПоследнийОбновленныйЭлемент.КлючДанных;
	
	Если СвойстваОграничения = Неопределено
	 Или СвойстваОграничения.ОпорныеПоля = Неопределено
	 Или СвойстваОграничения.ОпорныеПоля.Используемые.Количество() = 0
	 Или ТипЗнч(КлючДанных) <> Тип("Структура")
	 Или ВидКлючаДанных = "ЭлементыБезКлючейПоПериоду"
	   И (Не ПоследнийОбновленныйЭлемент.Свойство("Дата")
	      Или ТипЗнч(ПоследнийОбновленныйЭлемент.Дата) <> Тип("Дата"))
	 Или ВидКлючаДанных = "ЭлементыБезКлючейПоЗначениямПолей"
	   И СвойстваОграничения.ОпорныеПоля.Используемые.Количество() <> КлючДанных.Количество()
	 Или ВидКлючаДанных <> "ЭлементыБезКлючейПоПериоду"
	   И ВидКлючаДанных <> "ЭлементыБезКлючейПоЗначениямПолей"
	   И СвойстваОграничения.ОпорныеПоля.Используемые.Количество() > КлючДанных.Количество() Тогда
	
		Возврат
		"ВЫБРАТЬ
		|	-1 КАК Количество";
	КонецЕсли;
	
	ПоляВыбора = "";
	УсловиеОтбора = "";
	УсловиеСоединения = "";
	
	Если ЗначениеЗаполнено(СвойстваОграничения.ИмяОтдельногоРегистраКлючей) Тогда
		ИмяРегистраКлючей = СвойстваОграничения.ИмяОтдельногоРегистраКлючей;
	Иначе
		ИмяРегистраКлючей = "КлючиДоступаКРегистрам";
		ИмяПараметра = "ИдентификаторРегистра" + Формат(Индекс, "ЧГ=");
		УсловиеСоединения = "(КлючиДоступаКРегистрам.Регистр = &" + ИмяПараметра + ")";
		ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, Строка.Список);
	КонецЕсли;
	
	ОпорныеПоля = СвойстваОграничения.ОпорныеПоля;
	ОтборПоТипуПользователей = XMLСтрока(СвойстваОграничения.ВариантДоступа);
	УсловиеСоединения = УсловиеСоединения + ?(УсловиеСоединения = "", "", "
	|			И ") + "(КлючиДоступаКРегистрам.ВариантДоступа = " + ОтборПоТипуПользователей + ")"; // @query-part-1
	
	НомерПоля = 1;
	Для Каждого ИмяПоля Из ОпорныеПоля.Используемые Цикл
		ИмяПоляВКлючеДанных = "Поле" + НомерПоля;
		ИмяПараметра = ИмяПоля + Формат(Индекс, "ЧГ=");
		
		УсловиеСоединения = УсловиеСоединения + ?(УсловиеСоединения = "", "", "
		|			И ") + "(КлючиДоступаКРегистрам.Поле" + НомерПоля + " = ТекущаяТаблица." + ИмяПоля + ")"; // @query-part-1
		
		Отбор = "";
		Для ТекущийИндекс = 0 По НомерПоля - 2 Цикл
			ТекущееИмяПоля = ОпорныеПоля.Используемые[ТекущийИндекс];
			ТекущееИмяПараметра = ТекущееИмяПоля + Формат(Индекс, "ЧГ=");
			Отбор = Отбор + ?(Отбор = "", "", "
			|	И ") + "ТекущаяТаблица." + ТекущееИмяПоля + " = &" + ТекущееИмяПараметра; // @query-part-1
		КонецЦикла;
		Отбор = Отбор + ?(Отбор = "", "", "
		|	И ") + "ТекущаяТаблица." + ИмяПоля + " > &" + ИмяПараметра; // @query-part-1
		
		УсловиеОтбора = УсловиеОтбора + ?(НомерПоля = 1, ?(ОпорныеПоля.Используемые.Количество() > 1, "(", "") + Отбор, "
		|		ИЛИ " + ТекстСОтступом(Отбор, "		")); // @query-part-1
	
		Если КлючДанных.Свойство(ИмяПоляВКлючеДанных) Тогда
			ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, КлючДанных[ИмяПоляВКлючеДанных]);
		Иначе
			ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, Неопределено);
		КонецЕсли;
		ПоляВыбора = ПоляВыбора + ?(ПоляВыбора = "", "", ",
		|	") + "ТекущаяТаблица." + ИмяПоля;
		НомерПоля = НомерПоля + 1;
	КонецЦикла;
	
	Если ОпорныеПоля.Используемые.Количество() > 1 Тогда
		УсловиеОтбора  = УсловиеОтбора  + ")";
	КонецЕсли;
	
	Если ВидКлючаДанных = "ЭлементыБезКлючейПоПериоду" Тогда
		ИмяПоляПериода = ?(СвойстваТипа.ИмяКоллекции = "РегистрыРасчета", "ПериодРегистрации", "Период");
		ИмяПараметра = ИмяПоляПериода + Формат(Индекс, "ЧГ=");
		УсловиеОтбора = "(ТекущаяТаблица." + ИмяПоляПериода + " < &" + ИмяПараметра + "
		|	ИЛИ ТекущаяТаблица." // @query-part-1
		+ ИмяПоляПериода + " = &" + ИмяПараметра + "
		|		И " + ТекстСОтступом(УсловиеОтбора, "	") + ")"; // @query-part-1
		ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, ПоследнийОбновленныйЭлемент.Дата);
	КонецЕсли;
	
	Если СвойстваОграничения.ОпорныеПоля.Используемые.Количество() = 1 Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ &ПоляВыбора) КАК Количество
		|ИЗ
		|	&ТекущаяТаблица КАК ТекущаяТаблица
		|		ЛЕВОЕ СОЕДИНЕНИЕ ТекущийСписок КАК КлючиДоступаКРегистрам
		|		ПО (&УсловиеСоединения)
		|ГДЕ
		|	&УсловиеОтбора
		|	И КлючиДоступаКРегистрам.ВариантДоступа ЕСТЬ NULL";
	Иначе
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	КОЛИЧЕСТВО(*) КАК Количество
		|ИЗ
		|	(ВЫБРАТЬ РАЗЛИЧНЫЕ
		|		&ПоляВыбора КАК ПоляВыбора
		|	ИЗ
		|		&ТекущаяТаблица КАК ТекущаяТаблица
		|			ЛЕВОЕ СОЕДИНЕНИЕ ТекущийСписок КАК КлючиДоступаКРегистрам
		|			ПО (&УсловиеСоединения)
		|	ГДЕ
		|		&УсловиеОтбора
		|		И КлючиДоступаКРегистрам.ВариантДоступа ЕСТЬ NULL) КАК Комбинации";
		ПоляВыбора    = ТекстСОтступом(ПоляВыбора, "	");
		УсловиеОтбора = ТекстСОтступом(УсловиеОтбора, "	");
	КонецЕсли;
	
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "(&УсловиеСоединения)", УсловиеСоединения);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПоляВыбора",    ПоляВыбора);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", УсловиеОтбора);
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ТекущийСписок", "РегистрСведений." + ИмяРегистраКлючей);
	
	Возврат ТекстЗапроса;
	
КонецФункции

Процедура ЗаполнитьДолиКоличестваЭлементовРегистра(ДолиКоличестваЭлементов, Элемент, СвойстваОграничения)
	
	Если Элемент.ВидКлючаДанных = "ЭлементыСУстаревшимиКлючами" Тогда
		
		ДолиКоличестваЭлементов.Обработанных = 0.0;
		ДолиКоличестваЭлементов.Оставшихся   = 0.5;
		
	ИначеЕсли Элемент.ВидКлючаДанных = "ЭлементыБезКлючейПоЗначениямПолей"
	      Или Элемент.ВидКлючаДанных = "ЭлементыБезКлючейПоПериоду" Тогда
		
		Если Элемент.ОбработатьУстаревшиеЭлементы Тогда
			ДолиКоличестваЭлементов.Обработанных = 0.5;
			ДолиКоличестваЭлементов.Оставшихся   = 0.4;
		Иначе
			ДолиКоличестваЭлементов.Обработанных = 0.5;
			ДолиКоличестваЭлементов.Оставшихся   = 0.5;
		КонецЕсли;
		
	ИначеЕсли Элемент.ВидКлючаДанных = "УстаревшиеЭлементы" Тогда
		
		Если СвойстваОграничения <> Неопределено
		   И ЗначениеЗаполнено(СвойстваОграничения.ИмяОтдельногоРегистраКлючей) Тогда
			
			ДолиКоличестваЭлементов.Обработанных = 0.90;
			ДолиКоличестваЭлементов.Оставшихся   = 0.09;
		Иначе
			ДолиКоличестваЭлементов.Обработанных = 0.9;
			ДолиКоличестваЭлементов.Оставшихся   = 0.1;
		КонецЕсли;
		
	Иначе // УстаревшиеЭлементыОбщегоРегистра.
		ДолиКоличестваЭлементов.Обработанных = 0.99;
		ДолиКоличестваЭлементов.Оставшихся   = 0.01;
	КонецЕсли;
	
КонецПроцедуры

Процедура ДобавитьТекстЗапросаКоличестваКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, ДляВнешнихПользователей)
	
	ТекстЗапроса =
	"ВЫБРАТЬ
	|	КОЛИЧЕСТВО(КлючиДоступа.Ссылка) КАК Количество
	|ИЗ
	|	Справочник.КлючиДоступа КАК КлючиДоступа
	|ГДЕ
	|	КлючиДоступа.Список = &Список
	|	И КлючиДоступа.ДляВнешнихПользователей = ЛОЖЬ";
	
	ИмяПараметра = "СписокКлючей" + Формат(Индекс, "ЧГ=");
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&Список", "&" + ИмяПараметра);
	ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, Строка.Список);
	
	Если ДляВнешнихПользователей Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "ЛОЖЬ", "ИСТИНА");
	КонецЕсли;
	
	ОписаниеЗапроса.ТекстыПакетаЗапросов.Добавить(ТекстЗапроса);
	Индекс = Индекс + 1;
	
КонецПроцедуры

Процедура ДобавитьТекстЗапросаКоличестваОставшихсяКлючейДоступа(ОписаниеЗапроса, Строка, Индекс, Контекст, ДляВнешнихПользователей)
	
	СвойстваСписка = Контекст.СвойстваСписков.Получить(Строка.Список);
	
	Если СвойстваСписка = Неопределено Тогда
		ПараметрыЗадания = Неопределено;
		
	ИначеЕсли ДляВнешнихПользователей Тогда
		ПараметрыЗадания = СвойстваСписка.ПоследнийОбновленныйКлючДоступаДляВнешнихПользователей;
	Иначе
		ПараметрыЗадания = СвойстваСписка.ПоследнийОбновленныйКлючДоступа;
	КонецЕсли;
	
	Если ТипЗнч(ПараметрыЗадания) <> Тип("ХранилищеЗначения") Тогда
		ТекстЗапроса =
		"ВЫБРАТЬ
		|	НЕОПРЕДЕЛЕНО КАК Количество";
	Иначе
		ДолиКоличестваКлючейДоступа = Новый Структура("Обработанных, Оставшихся", 0, 1);
		ПараметрыЗадания = ПараметрыЗадания.Получить();
		
		Если ТипЗнч(ПараметрыЗадания) = Тип("Структура")
		   И ПараметрыЗадания.Свойство("ПоследнийОбновленныйЭлемент")
		   И ТипЗнч(ПараметрыЗадания.ПоследнийОбновленныйЭлемент) = Тип("Структура")
		   И ПараметрыЗадания.ПоследнийОбновленныйЭлемент.Свойство("ВидКлючаДанных")
		   И ПорядокВидаКлючаДанных(ПараметрыЗадания.ПоследнийОбновленныйЭлемент.ВидКлючаДанных) <> Неопределено
		   И ПараметрыЗадания.ПоследнийОбновленныйЭлемент.Свойство("КлючДанных") Тогда
			
			ПоследнийКлючДоступа = ПараметрыЗадания.ПоследнийОбновленныйЭлемент.КлючДанных;
			ЗаполнитьДолиКоличестваКлючейДоступа(ДолиКоличестваКлючейДоступа,
				ПараметрыЗадания.ПоследнийОбновленныйЭлемент);
		КонецЕсли;
		УсловиеОтбора = "";
		Если ТипЗнч(ПоследнийКлючДоступа) = Тип("СправочникСсылка.КлючиДоступа") Тогда
			ИмяПараметра = "СписокОбработанныхКлючей" + Формат(Индекс, "ЧГ=");
			УсловиеОтбора = "КлючиДоступа.Список = &" + ИмяПараметра;
			ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, Строка.Список);
			
			УсловиеОтбора = УсловиеОтбора + "
			|	И КлючиДоступа.ДляВнешнихПользователей = " + ?(ДляВнешнихПользователей, "ИСТИНА", "ЛОЖЬ"); // @query-part-1
			
			ИмяПараметра = "ПоследнийКлючДоступа" + Формат(Индекс, "ЧГ=");
			УсловиеОтбора = УсловиеОтбора + "
			|	И КлючиДоступа.Ссылка > &" + ИмяПараметра; // @query-part-1
			ОписаниеЗапроса.Запрос.УстановитьПараметр(ИмяПараметра, ПоследнийКлючДоступа);
		КонецЕсли;
		
		Если ЗначениеЗаполнено(УсловиеОтбора) Тогда
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	КОЛИЧЕСТВО(*) КАК Количество
			|ИЗ
			|	Справочник.КлючиДоступа КАК КлючиДоступа
			|ГДЕ
			|	&УсловиеОтбора";
			ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеОтбора", УсловиеОтбора);
		Иначе
			ТекстЗапроса =
			"ВЫБРАТЬ
			|	-1 КАК Количество";
		КонецЕсли;
		
		Если ДляВнешнихПользователей Тогда
			Строка.ДоляОбработанныхКлючейДоступаДляВнешнихПользователей = ДолиКоличестваКлючейДоступа.Обработанных;
			Строка.ДоляОставшихсяКлючейДоступаДляВнешнихПользователей   = ДолиКоличестваКлючейДоступа.Оставшихся;
		Иначе
			Строка.ДоляОбработанныхКлючейДоступаДляПользователей = ДолиКоличестваКлючейДоступа.Обработанных;
			Строка.ДоляОставшихсяКлючейДоступаДляПользователей   = ДолиКоличестваКлючейДоступа.Оставшихся;
		КонецЕсли;
	КонецЕсли;
	
	ОписаниеЗапроса.ТекстыПакетаЗапросов.Добавить(ТекстЗапроса);
	Индекс = Индекс + 1;
	
КонецПроцедуры

Процедура ЗаполнитьДолиКоличестваКлючейДоступа(ДолиКоличестваКлючейДоступа, Элемент)
	
	Если Элемент.ВидКлючаДанных = "ЭлементыСУстаревшимиПравами" Тогда
		Если Элемент.ОбработатьУстаревшиеЭлементы Тогда
			ДолиКоличестваКлючейДоступа.Обработанных = 0.0;
			ДолиКоличестваКлючейДоступа.Оставшихся   = 0.9;
		Иначе
			ДолиКоличестваКлючейДоступа.Обработанных = 0.0;
			ДолиКоличестваКлючейДоступа.Оставшихся   = 1.0;
		КонецЕсли;
	Иначе // УстаревшиеЭлементы.
		ДолиКоличестваКлючейДоступа.Обработанных = 0.9;
		ДолиКоличестваКлючейДоступа.Оставшихся   = 0.1;
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  ДействующиеПараметры - см. ДействующиеПараметрыОграниченияДоступа
//  
// Возвращаемое значение:
//   см. ОбщегоНазначения.ИдентификаторыОбъектовМетаданных
// 
Функция ИдентификаторыСписковСОграничением(ДействующиеПараметры)
	
	ДействующиеПараметры = ДействующиеПараметрыОграниченияДоступа(Неопределено, Неопределено, Ложь);
	
	Списки = Новый Массив;
	Для Каждого ОписаниеВерсии Из ДействующиеПараметры.ВерсииОграниченийСписков Цикл
		Списки.Добавить(ОписаниеВерсии.Ключ);
	КонецЦикла;
	
	Возврат ОбщегоНазначения.ИдентификаторыОбъектовМетаданных(Списки, Ложь);
	
КонецФункции

Функция ТекущаяДатаНаСервере() Экспорт
	
	// АПК:143-выкл - №643.2.1 Требуется ТекущаяДата сервера, а не ТекущаяДатаСеанса,
	// так как именно ТекущаяДата записывается в журнал регистрации.
	Возврат ТекущаяДата();
	// АПК:143-вкл.
	
КонецФункции

#КонецОбласти

#КонецОбласти

#КонецОбласти
